tango-app-api-audio-analytics 1.0.31 → 1.0.33

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.
@@ -1,235 +1,235 @@
1
- #!/bin/bash
2
-
3
- # Cohort API Test Examples
4
- # This file contains curl commands to test all cohort API endpoints
5
-
6
- BASE_URL="https://testtangoretail-api.tangoeye.ai/v3/audio-analitics"
7
- COHORT_ID="cohort_photochromatic_001"
8
- CLIENT_ID="11"
9
-
10
- # ==================== Helper Function ====================
11
- print_section() {
12
- echo ""
13
- echo "=================================="
14
- echo "$1"
15
- echo "=================================="
16
- }
17
-
18
- # ==================== 1. CREATE COHORT ====================
19
- print_section "1. CREATE COHORT"
20
-
21
- curl -X POST "$BASE_URL/cohorts" \
22
- -H "Content-Type: application/json" \
23
- -d '{
24
- "clientId": "11",
25
- "cohortId": "cohort_photochromatic_001",
26
- "cohortName": "Photochromatic",
27
- "cohortDescription": "A strict evaluation of the photochromatic sales journey, focusing on pitch quality, technical accuracy, and customer sentiment",
28
- "metrics": [
29
- {
30
- "metricId": "pitch_quality_001",
31
- "metricName": "Standard Pitch Quality",
32
- "metricDescription": "Identify which specific value propositions were explicitly used by the staff",
33
- "hasContext": true,
34
- "isNumneric": false,
35
- "contexts": [
36
- {
37
- "contextName": "ONE_GLASS_ALL_NEEDS",
38
- "priority": 2,
39
- "description": "Convenience of one pair for all lighting"
40
- },
41
- {
42
- "contextName": "TRAVEL_CONVENIENCE",
43
- "priority": 2,
44
- "description": "Specific benefits for bikers/commuters"
45
- },
46
- {
47
- "contextName": "LIGHT_ADAPTATION",
48
- "priority": 2,
49
- "description": "Technical explanation of UV reaction"
50
- },
51
- {
52
- "contextName": "REDUCES_EYE_STRAIN",
53
- "priority": 2,
54
- "description": "Glare and fatigue protection"
55
- }
56
- ]
57
- },
58
- {
59
- "metricId": "technical_depth_score_001",
60
- "metricName": "Technical Depth Score",
61
- "metricDescription": "A numeric evaluation of the staff'\''s product knowledge during the pitch",
62
- "hasContext": true,
63
- "isNumneric": true,
64
- "contexts": [
65
- {
66
- "minValue": 0,
67
- "maxValue": 100
68
- }
69
- ]
70
- },
71
- {
72
- "metricId": "customer_sentiment_001",
73
- "metricName": "Initial Customer Sentiment",
74
- "metricDescription": "The immediate reaction of the customer when the lens was introduced",
75
- "hasContext": true,
76
- "isNumneric": false,
77
- "contexts": [
78
- {
79
- "contextName": "POSITIVE",
80
- "priority": 2,
81
- "description": "High interest or curiosity"
82
- },
83
- {
84
- "contextName": "NEUTRAL",
85
- "priority": 1,
86
- "description": "Acknowledgment or price-focused only"
87
- },
88
- {
89
- "contextName": "NEGATIVE",
90
- "priority": 0,
91
- "description": "Dismissive or disinterested"
92
- }
93
- ]
94
- },
95
- {
96
- "metricId": "staff_responsiveness_001",
97
- "metricName": "Staff Responsiveness",
98
- "metricDescription": "How effectively were customer doubts addressed?",
99
- "hasContext": true,
100
- "isNumneric": false,
101
- "contexts": [
102
- {
103
- "contextName": "FULLY_RESPONSIVE",
104
- "priority": 2,
105
- "description": "All doubts cleared with evidence"
106
- },
107
- {
108
- "contextName": "PARTIALLY_RESPONSIVE",
109
- "priority": 1,
110
- "description": "Some doubts addressed, others ignored"
111
- },
112
- {
113
- "contextName": "UNRESPONSIVE",
114
- "priority": 0,
115
- "description": "Customer doubts were dismissed"
116
- }
117
- ]
118
- },
119
- {
120
- "metricId": "sales_outcome_001",
121
- "metricName": "Final Sales Outcome",
122
- "metricDescription": "The final commitment level reached at the end of the conversation",
123
- "hasContext": true,
124
- "isNumneric": false,
125
- "contexts": [
126
- {
127
- "contextName": "BOUGHT_PHOTOCHROMATIC",
128
- "priority": 2,
129
- "description": "Customer agreed to the upgrade"
130
- },
131
- {
132
- "contextName": "BOUGHT_SOMETHING_ELSE",
133
- "priority": 0,
134
- "description": "Bought standard lenses/frames only"
135
- },
136
- {
137
- "contextName": "NO_SALE",
138
- "priority": 0,
139
- "description": "No purchase commitment"
140
- }
141
- ]
142
- },
143
- {
144
- "metricId": "pitch_timing_summary_001",
145
- "metricName": "Pitch Timing & Context",
146
- "metricDescription": "Narrative summary of when and how the pitch was introduced",
147
- "hasContext": false,
148
- "isNumneric": false
149
- }
150
- ]
151
- }'
152
-
153
- # ==================== 2. GET COHORT BY ID ====================
154
- print_section "2. GET COHORT BY ID"
155
-
156
- curl -X GET "$BASE_URL/cohorts/$COHORT_ID" \
157
- -H "Content-Type: application/json"
158
-
159
- # ==================== 3. GET COHORTS BY CLIENT ====================
160
- print_section "3. GET COHORTS BY CLIENT"
161
-
162
- curl -X GET "$BASE_URL/cohorts/client/$CLIENT_ID?limit=10&offset=0" \
163
- -H "Content-Type: application/json"
164
-
165
- # ==================== 4. SEARCH COHORTS ====================
166
- print_section "4. SEARCH COHORTS"
167
-
168
- curl -X POST "$BASE_URL/cohorts/search" \
169
- -H "Content-Type: application/json" \
170
- -d '{
171
- "clientId": "11",
172
- "cohortName": "Photochrom",
173
- "limit": 10,
174
- "offset": 0
175
- }'
176
-
177
- # ==================== 5. UPDATE COHORT ====================
178
- print_section "5. UPDATE COHORT"
179
-
180
- # Note: Replace DOCUMENT_ID with actual ID returned from create
181
- DOCUMENT_ID="550e8400-e29b-41d4-a716-446655440000"
182
-
183
- curl -X PUT "$BASE_URL/cohorts/$DOCUMENT_ID" \
184
- -H "Content-Type: application/json" \
185
- -d '{
186
- "cohortName": "Updated Photochromatic",
187
- "cohortDescription": "Updated description for photochromatic cohort"
188
- }'
189
-
190
- # ==================== 6. GET COHORT ANALYTICS ====================
191
- print_section "6. GET COHORT ANALYTICS"
192
-
193
- curl -X GET "$BASE_URL/cohorts/$COHORT_ID/analytics" \
194
- -H "Content-Type: application/json"
195
-
196
- # ==================== 7. DELETE COHORT ====================
197
- print_section "7. DELETE COHORT"
198
-
199
- # Note: Replace DOCUMENT_ID with actual ID
200
- curl -X DELETE "$BASE_URL/cohorts/$DOCUMENT_ID" \
201
- -H "Content-Type: application/json"
202
-
203
- # ==================== VALIDATION TESTS ====================
204
- print_section "VALIDATION TESTS"
205
-
206
- echo "Testing invalid cohort name (too short):"
207
- curl -X POST "$BASE_URL/cohorts" \
208
- -H "Content-Type: application/json" \
209
- -d '{
210
- "clientId": "11",
211
- "cohortId": "invalid_test",
212
- "cohortName": "AB",
213
- "cohortDescription": "This should fail because name is too short"
214
- }'
215
-
216
- echo ""
217
- echo "Testing missing required field:"
218
- curl -X POST "$BASE_URL/cohorts" \
219
- -H "Content-Type: application/json" \
220
- -d '{
221
- "clientId": "11",
222
- "cohortId": "invalid_test",
223
- "cohortName": "Valid Name"
224
- }'
225
-
226
- echo ""
227
- echo "Testing invalid cohortId (special characters):"
228
- curl -X POST "$BASE_URL/cohorts" \
229
- -H "Content-Type: application/json" \
230
- -d '{
231
- "clientId": "11",
232
- "cohortId": "invalid@test",
233
- "cohortName": "Valid Name",
234
- "cohortDescription": "This should fail because cohortId has invalid characters"
235
- }'
1
+ #!/bin/bash
2
+
3
+ # Cohort API Test Examples
4
+ # This file contains curl commands to test all cohort API endpoints
5
+
6
+ BASE_URL="https://testtangoretail-api.tangoeye.ai/v3/audio-analitics"
7
+ COHORT_ID="cohort_photochromatic_001"
8
+ CLIENT_ID="11"
9
+
10
+ # ==================== Helper Function ====================
11
+ print_section() {
12
+ echo ""
13
+ echo "=================================="
14
+ echo "$1"
15
+ echo "=================================="
16
+ }
17
+
18
+ # ==================== 1. CREATE COHORT ====================
19
+ print_section "1. CREATE COHORT"
20
+
21
+ curl -X POST "$BASE_URL/cohorts" \
22
+ -H "Content-Type: application/json" \
23
+ -d '{
24
+ "clientId": "11",
25
+ "cohortId": "cohort_photochromatic_001",
26
+ "cohortName": "Photochromatic",
27
+ "cohortDescription": "A strict evaluation of the photochromatic sales journey, focusing on pitch quality, technical accuracy, and customer sentiment",
28
+ "metrics": [
29
+ {
30
+ "metricId": "pitch_quality_001",
31
+ "metricName": "Standard Pitch Quality",
32
+ "metricDescription": "Identify which specific value propositions were explicitly used by the staff",
33
+ "hasContext": true,
34
+ "isNumneric": false,
35
+ "contexts": [
36
+ {
37
+ "contextName": "ONE_GLASS_ALL_NEEDS",
38
+ "priority": 2,
39
+ "description": "Convenience of one pair for all lighting"
40
+ },
41
+ {
42
+ "contextName": "TRAVEL_CONVENIENCE",
43
+ "priority": 2,
44
+ "description": "Specific benefits for bikers/commuters"
45
+ },
46
+ {
47
+ "contextName": "LIGHT_ADAPTATION",
48
+ "priority": 2,
49
+ "description": "Technical explanation of UV reaction"
50
+ },
51
+ {
52
+ "contextName": "REDUCES_EYE_STRAIN",
53
+ "priority": 2,
54
+ "description": "Glare and fatigue protection"
55
+ }
56
+ ]
57
+ },
58
+ {
59
+ "metricId": "technical_depth_score_001",
60
+ "metricName": "Technical Depth Score",
61
+ "metricDescription": "A numeric evaluation of the staff'\''s product knowledge during the pitch",
62
+ "hasContext": true,
63
+ "isNumneric": true,
64
+ "contexts": [
65
+ {
66
+ "minValue": 0,
67
+ "maxValue": 100
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ "metricId": "customer_sentiment_001",
73
+ "metricName": "Initial Customer Sentiment",
74
+ "metricDescription": "The immediate reaction of the customer when the lens was introduced",
75
+ "hasContext": true,
76
+ "isNumneric": false,
77
+ "contexts": [
78
+ {
79
+ "contextName": "POSITIVE",
80
+ "priority": 2,
81
+ "description": "High interest or curiosity"
82
+ },
83
+ {
84
+ "contextName": "NEUTRAL",
85
+ "priority": 1,
86
+ "description": "Acknowledgment or price-focused only"
87
+ },
88
+ {
89
+ "contextName": "NEGATIVE",
90
+ "priority": 0,
91
+ "description": "Dismissive or disinterested"
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "metricId": "staff_responsiveness_001",
97
+ "metricName": "Staff Responsiveness",
98
+ "metricDescription": "How effectively were customer doubts addressed?",
99
+ "hasContext": true,
100
+ "isNumneric": false,
101
+ "contexts": [
102
+ {
103
+ "contextName": "FULLY_RESPONSIVE",
104
+ "priority": 2,
105
+ "description": "All doubts cleared with evidence"
106
+ },
107
+ {
108
+ "contextName": "PARTIALLY_RESPONSIVE",
109
+ "priority": 1,
110
+ "description": "Some doubts addressed, others ignored"
111
+ },
112
+ {
113
+ "contextName": "UNRESPONSIVE",
114
+ "priority": 0,
115
+ "description": "Customer doubts were dismissed"
116
+ }
117
+ ]
118
+ },
119
+ {
120
+ "metricId": "sales_outcome_001",
121
+ "metricName": "Final Sales Outcome",
122
+ "metricDescription": "The final commitment level reached at the end of the conversation",
123
+ "hasContext": true,
124
+ "isNumneric": false,
125
+ "contexts": [
126
+ {
127
+ "contextName": "BOUGHT_PHOTOCHROMATIC",
128
+ "priority": 2,
129
+ "description": "Customer agreed to the upgrade"
130
+ },
131
+ {
132
+ "contextName": "BOUGHT_SOMETHING_ELSE",
133
+ "priority": 0,
134
+ "description": "Bought standard lenses/frames only"
135
+ },
136
+ {
137
+ "contextName": "NO_SALE",
138
+ "priority": 0,
139
+ "description": "No purchase commitment"
140
+ }
141
+ ]
142
+ },
143
+ {
144
+ "metricId": "pitch_timing_summary_001",
145
+ "metricName": "Pitch Timing & Context",
146
+ "metricDescription": "Narrative summary of when and how the pitch was introduced",
147
+ "hasContext": false,
148
+ "isNumneric": false
149
+ }
150
+ ]
151
+ }'
152
+
153
+ # ==================== 2. GET COHORT BY ID ====================
154
+ print_section "2. GET COHORT BY ID"
155
+
156
+ curl -X GET "$BASE_URL/cohorts/$COHORT_ID" \
157
+ -H "Content-Type: application/json"
158
+
159
+ # ==================== 3. GET COHORTS BY CLIENT ====================
160
+ print_section "3. GET COHORTS BY CLIENT"
161
+
162
+ curl -X GET "$BASE_URL/cohorts/client/$CLIENT_ID?limit=10&offset=0" \
163
+ -H "Content-Type: application/json"
164
+
165
+ # ==================== 4. SEARCH COHORTS ====================
166
+ print_section "4. SEARCH COHORTS"
167
+
168
+ curl -X POST "$BASE_URL/cohorts/search" \
169
+ -H "Content-Type: application/json" \
170
+ -d '{
171
+ "clientId": "11",
172
+ "cohortName": "Photochrom",
173
+ "limit": 10,
174
+ "offset": 0
175
+ }'
176
+
177
+ # ==================== 5. UPDATE COHORT ====================
178
+ print_section "5. UPDATE COHORT"
179
+
180
+ # Note: Replace DOCUMENT_ID with actual ID returned from create
181
+ DOCUMENT_ID="550e8400-e29b-41d4-a716-446655440000"
182
+
183
+ curl -X PUT "$BASE_URL/cohorts/$DOCUMENT_ID" \
184
+ -H "Content-Type: application/json" \
185
+ -d '{
186
+ "cohortName": "Updated Photochromatic",
187
+ "cohortDescription": "Updated description for photochromatic cohort"
188
+ }'
189
+
190
+ # ==================== 6. GET COHORT ANALYTICS ====================
191
+ print_section "6. GET COHORT ANALYTICS"
192
+
193
+ curl -X GET "$BASE_URL/cohorts/$COHORT_ID/analytics" \
194
+ -H "Content-Type: application/json"
195
+
196
+ # ==================== 7. DELETE COHORT ====================
197
+ print_section "7. DELETE COHORT"
198
+
199
+ # Note: Replace DOCUMENT_ID with actual ID
200
+ curl -X DELETE "$BASE_URL/cohorts/$DOCUMENT_ID" \
201
+ -H "Content-Type: application/json"
202
+
203
+ # ==================== VALIDATION TESTS ====================
204
+ print_section "VALIDATION TESTS"
205
+
206
+ echo "Testing invalid cohort name (too short):"
207
+ curl -X POST "$BASE_URL/cohorts" \
208
+ -H "Content-Type: application/json" \
209
+ -d '{
210
+ "clientId": "11",
211
+ "cohortId": "invalid_test",
212
+ "cohortName": "AB",
213
+ "cohortDescription": "This should fail because name is too short"
214
+ }'
215
+
216
+ echo ""
217
+ echo "Testing missing required field:"
218
+ curl -X POST "$BASE_URL/cohorts" \
219
+ -H "Content-Type: application/json" \
220
+ -d '{
221
+ "clientId": "11",
222
+ "cohortId": "invalid_test",
223
+ "cohortName": "Valid Name"
224
+ }'
225
+
226
+ echo ""
227
+ echo "Testing invalid cohortId (special characters):"
228
+ curl -X POST "$BASE_URL/cohorts" \
229
+ -H "Content-Type: application/json" \
230
+ -d '{
231
+ "clientId": "11",
232
+ "cohortId": "invalid@test",
233
+ "cohortName": "Valid Name",
234
+ "cohortDescription": "This should fail because cohortId has invalid characters"
235
+ }'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-audio-analytics",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "audioAnalytics",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -8,7 +8,7 @@
8
8
  "start": "nodemon --exec \"eslint --fix . && node app.js\""
9
9
  },
10
10
  "engines": {
11
- "node": ">=18.10.0"
11
+ "node": ">=18.10.0 <25"
12
12
  },
13
13
  "author": "praveenraj",
14
14
  "license": "ISC",
@@ -329,7 +329,6 @@ export async function getChat( req, res ) {
329
329
  export const callExternalStreamAPI = async ( req, res ) => {
330
330
  try {
331
331
  const { prompt, storeId, productModule, country, region, fromDate, toDate, clusters } = req.body;
332
- console.log( 'Received request for external stream API with parameters:', req.body );
333
332
  // Validation for required parameters
334
333
  if ( !prompt ) {
335
334
  logger.warn( 'Stream API call attempted without prompt' );
@@ -1,6 +1,6 @@
1
1
  // import { sampleCohortData } from '../models/cohortAnalysis.model.js';
2
2
  import { logger } from 'tango-app-api-middleware';
3
- import { getCohortAnalysisSummaryCard } from '../services/conversation.service.js';
3
+ import { getCohortAnalysisSummaryCard, getCohortSummaryCardFromLambda } from '../services/conversation.service.js';
4
4
 
5
5
  /**
6
6
  * Get Cohort Analysis Summary Card
@@ -71,6 +71,74 @@ export const getCohortAnalysisCard = async ( req, res ) => {
71
71
  }
72
72
  };
73
73
 
74
+ /**
75
+ * Get Cohort Summary Card
76
+ * POST /cohort-summary-card
77
+ * @param {Object} req - Express request object
78
+ * @param {Object} req.body - Request body
79
+ * @param {string} req.body.startDate - Start date (YYYY-MM-DD)
80
+ * @param {string} req.body.endDate - End date (YYYY-MM-DD)
81
+ * @param {string[]} req.body.storeId - Array of store IDs
82
+ * @param {string[]} req.body.clientId - Array of client IDs
83
+ * @param {string[]} req.body.cohortId - Array of cohort IDs
84
+ * @param {Object} res - Express response object
85
+ * @return {void} Returns JSON response with cohort summary card data
86
+ */
87
+ export const getCohortSummaryCard = async ( req, res ) => {
88
+ try {
89
+ const { startDate, endDate, storeId, clientId, cohortId } = req.body;
90
+
91
+ if ( !startDate || !endDate || !storeId || !clientId || !cohortId ) {
92
+ return res.status( 400 ).json( {
93
+ status: 'error',
94
+ message: 'Missing required parameters: startDate, endDate, storeId, clientId, cohortId',
95
+ code: 'MISSING_PARAMETERS',
96
+ timestamp: new Date().toISOString(),
97
+ } );
98
+ }
99
+
100
+ logger.info( {
101
+ message: 'Fetching cohort summary card',
102
+ startDate,
103
+ endDate,
104
+ storeId,
105
+ clientId,
106
+ cohortId,
107
+ } );
108
+
109
+ const lambdaResponse = await getCohortSummaryCardFromLambda( {
110
+ startDate,
111
+ endDate,
112
+ storeId,
113
+ clientId,
114
+ cohortId,
115
+ } );
116
+
117
+ if ( !lambdaResponse ) {
118
+ return res.status( 204 ).json( {
119
+ status: 'error',
120
+ message: 'No data found for the given parameters',
121
+ code: 'DATA_NOT_FOUND',
122
+ timestamp: new Date().toISOString(),
123
+ } );
124
+ }
125
+
126
+ return res.status( 200 ).json( {
127
+ status: lambdaResponse?.status || 'success',
128
+ data: lambdaResponse?.data ?? lambdaResponse,
129
+ timestamp: new Date().toISOString(),
130
+ } );
131
+ } catch ( error ) {
132
+ logger.error( { error, message: 'Error fetching cohort summary card', body: req.body } );
133
+ return res.status( 500 ).json( {
134
+ status: 'error',
135
+ message: error.message || 'Internal server error',
136
+ code: 'INTERNAL_ERROR',
137
+ timestamp: new Date().toISOString(),
138
+ } );
139
+ }
140
+ };
141
+
74
142
  /**
75
143
  * Get Complete Cohort Analysis
76
144
  * GET /cohort-analysis
@@ -5,6 +5,7 @@ import {
5
5
  exportConversationsFromLambda,
6
6
  getConversationDetailsFromLambda,
7
7
  getTranscriptListFromLambda,
8
+ getCategoryAnalysisCardFromLambda,
8
9
  // filterConversationsBySearch,
9
10
  // sortConversations,
10
11
  } from '../services/conversation.service.js';
@@ -38,6 +39,7 @@ export const getConversationsList = async ( req, res ) => {
38
39
  isAI,
39
40
  analyticsType,
40
41
  searchValue,
42
+ audioSource,
41
43
  limit,
42
44
  offset,
43
45
  isExport,
@@ -80,6 +82,7 @@ export const getConversationsList = async ( req, res ) => {
80
82
  offset,
81
83
  _id,
82
84
  name,
85
+ audioSource,
83
86
  } );
84
87
 
85
88
  // conversations = exportResponse.conversations || [];
@@ -118,6 +121,7 @@ export const getConversationsList = async ( req, res ) => {
118
121
  isAI,
119
122
  analyticsType,
120
123
  searchValue,
124
+ audioSource,
121
125
  limit,
122
126
  offset,
123
127
  _id,
@@ -188,6 +192,7 @@ export const getTranscriptsList = async ( req, res ) => {
188
192
  isAI,
189
193
  analyticsType,
190
194
  searchValue,
195
+ audioSource,
191
196
  limit,
192
197
  offset,
193
198
  isExport,
@@ -227,6 +232,7 @@ export const getTranscriptsList = async ( req, res ) => {
227
232
  isAI,
228
233
  analyticsType,
229
234
  searchValue,
235
+ audioSource,
230
236
  limit,
231
237
  offset,
232
238
  _id,
@@ -334,3 +340,77 @@ export const getConversationDetails = async ( req, res ) => {
334
340
  }
335
341
  };
336
342
 
343
+ /**
344
+ * Get Category Analysis Card
345
+ * POST /category-analysis-card
346
+ * @param {Object} req - Express request object
347
+ * @param {Object} req.body - Request body
348
+ * @param {string} req.body.startDate - Start date (YYYY-MM-DD)
349
+ * @param {string} req.body.endDate - End date (YYYY-MM-DD)
350
+ * @param {string[]} req.body.storeId - Array of store IDs
351
+ * @param {string} req.body.cohortId - Cohort ID
352
+ * @param {string} req.body.clientId - Client ID
353
+ * @param {number} req.body.id - Category numeric ID
354
+ * @param {string} req.body.category - Category name
355
+ * @param {Object} res - Express response object
356
+ * @return {void} Returns JSON response with category analysis card data
357
+ */
358
+ export const getCategoryAnalysisCard = async ( req, res ) => {
359
+ try {
360
+ const { startDate, endDate, storeId, cohortId, clientId, id, category } = req.body;
361
+
362
+ if ( !startDate || !endDate || !storeId || !cohortId || !clientId || id === undefined || !category ) {
363
+ return res.status( 400 ).json( {
364
+ status: 'error',
365
+ message: 'Missing required parameters: startDate, endDate, storeId, cohortId, clientId, id, category',
366
+ code: 'MISSING_PARAMETERS',
367
+ timestamp: new Date().toISOString(),
368
+ } );
369
+ }
370
+
371
+ logger.info( {
372
+ message: 'Fetching category analysis card',
373
+ startDate,
374
+ endDate,
375
+ storeId,
376
+ cohortId,
377
+ clientId,
378
+ id,
379
+ category,
380
+ } );
381
+
382
+ const lambdaResponse = await getCategoryAnalysisCardFromLambda( {
383
+ startDate,
384
+ endDate,
385
+ storeId,
386
+ cohortId,
387
+ clientId,
388
+ id,
389
+ category,
390
+ } );
391
+
392
+ if ( !lambdaResponse ) {
393
+ return res.status( 204 ).json( {
394
+ status: 'error',
395
+ message: 'No data found for the given parameters',
396
+ code: 'DATA_NOT_FOUND',
397
+ timestamp: new Date().toISOString(),
398
+ } );
399
+ }
400
+
401
+ return res.status( 200 ).json( {
402
+ status: lambdaResponse?.status || 'success',
403
+ data: lambdaResponse?.data ?? lambdaResponse,
404
+ timestamp: new Date().toISOString(),
405
+ } );
406
+ } catch ( error ) {
407
+ logger.error( { error, message: 'Error fetching category analysis card', body: req.body } );
408
+ return res.status( 500 ).json( {
409
+ status: 'error',
410
+ message: error.message || 'Internal server error',
411
+ code: 'INTERNAL_ERROR',
412
+ timestamp: new Date().toISOString(),
413
+ } );
414
+ }
415
+ };
416
+
@@ -510,6 +510,30 @@ export const cohortAnalysisCardValid = joi.object( {
510
510
  clientId: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of client IDs' ),
511
511
  } ).strict();
512
512
 
513
+ /**
514
+ * Cohort Summary Card Schema
515
+ */
516
+ export const cohortSummaryCardValid = joi.object( {
517
+ startDate: joi.string().required().pattern( /^\d{4}-\d{2}-\d{2}$/ ).description( 'Start date in YYYY-MM-DD format' ),
518
+ endDate: joi.string().required().pattern( /^\d{4}-\d{2}-\d{2}$/ ).description( 'End date in YYYY-MM-DD format' ),
519
+ storeId: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of store IDs' ),
520
+ clientId: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of client IDs' ),
521
+ cohortId: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of cohort IDs' ),
522
+ } ).strict();
523
+
524
+ /**
525
+ * Category Analysis Card Schema
526
+ */
527
+ export const categoryAnalysisCardValid = joi.object( {
528
+ startDate: joi.string().required().pattern( /^\d{4}-\d{2}-\d{2}$/ ).description( 'Start date in YYYY-MM-DD format' ),
529
+ endDate: joi.string().required().pattern( /^\d{4}-\d{2}-\d{2}$/ ).description( 'End date in YYYY-MM-DD format' ),
530
+ storeId: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of store IDs' ),
531
+ cohortId: joi.string().required().description( 'Cohort ID' ),
532
+ clientId: joi.string().required().description( 'Client ID' ),
533
+ id: joi.number().integer().required().description( 'Category numeric ID' ),
534
+ category: joi.string().required().description( 'Category name' ),
535
+ } ).strict();
536
+
513
537
  /**
514
538
  * Conversations List Schema
515
539
  */
@@ -522,6 +546,7 @@ export const conversationsListValid = joi.object( {
522
546
  isAI: joi.boolean().optional().description( 'Filter for AI-generated conversations' ),
523
547
  analyticsType: joi.string().optional().valid( 'audio', 'text', 'all' ).description( 'Type of analytics' ),
524
548
  searchValue: joi.string().optional().description( 'Search term' ),
549
+ audioSource: joi.string().optional().description( 'Audio source filter' ),
525
550
  isExport: joi.boolean().optional().description( 'Flag to export as CSV' ),
526
551
  limit: joi.number().integer().min( 1 ).max( 1000 ).optional().default( 10 ).description( 'Pagination limit' ),
527
552
  offset: joi.number().integer().min( 0 ).optional().default( 0 ).description( 'Pagination offset' ),
@@ -539,6 +564,7 @@ export const transcriptListValid = joi.object( {
539
564
  isAI: joi.boolean().optional().description( 'Filter for AI-generated conversations' ),
540
565
  analyticsType: joi.string().optional().valid( 'audio', 'text', 'all' ).description( 'Type of analytics' ),
541
566
  searchValue: joi.string().optional().description( 'Search term' ),
567
+ audioSource: joi.string().optional().description( 'Audio source filter' ),
542
568
  isExport: joi.boolean().optional().description( 'Flag to export as CSV' ),
543
569
  limit: joi.number().integer().min( 1 ).max( 1000 ).optional().default( 10 ).description( 'Pagination limit' ),
544
570
  offset: joi.number().integer().min( 0 ).optional().default( 0 ).description( 'Pagination offset' ),
@@ -2,10 +2,10 @@
2
2
  import express from 'express';
3
3
  import { validate } from 'tango-app-api-middleware';
4
4
  import { createCohortValid, createBulkCohortValid, updateCohortValid, getCohortValid, listCohortsByClientValid, deleteCohortValid } from '../dtos/audioAnalytics.dtos.js';
5
- import { cohortAnalysisCardValid, conversationsListValid, conversationDetailsValid, chatHistoryListValid, getChatValid, getGeminiResponseValid, transcriptListValid } from '../dtos/audioAnalytics.dtos.js';
5
+ import { cohortAnalysisCardValid, cohortSummaryCardValid, categoryAnalysisCardValid, conversationsListValid, conversationDetailsValid, chatHistoryListValid, getChatValid, getGeminiResponseValid, transcriptListValid } from '../dtos/audioAnalytics.dtos.js';
6
6
  import { createCohort, createBulkCohort, updateCohort, deleteCohort, getCohort, listCohortsByClient, chatHistoryList, getChat, getGeminiResponse, aiStreamResponse } from '../controllers/audioAnalytics.controller.js';
7
- import { getCohortAnalysisCard } from '../controllers/cohortAnalytics.controller.js';
8
- import { getConversationsList, getTranscriptsList, getConversationDetails } from '../controllers/conversationAnalytics.controller.js';
7
+ import { getCohortAnalysisCard, getCohortSummaryCard } from '../controllers/cohortAnalytics.controller.js';
8
+ import { getConversationsList, getTranscriptsList, getConversationDetails, getCategoryAnalysisCard } from '../controllers/conversationAnalytics.controller.js';
9
9
 
10
10
  export const audioAnalyticsrouter = express.Router(); ;
11
11
 
@@ -27,6 +27,8 @@ audioAnalyticsrouter.post( '/chat-history-list', validate( chatHistoryListValid
27
27
  audioAnalyticsrouter.post( '/get-chat', validate( getChatValid ), getChat );
28
28
  // Cohort Analytics Routes
29
29
  audioAnalyticsrouter.post( '/cohort-analysis-card', validate( cohortAnalysisCardValid ), getCohortAnalysisCard );
30
+ audioAnalyticsrouter.post( '/cohort-summary-card', validate( cohortSummaryCardValid ), getCohortSummaryCard );
31
+ audioAnalyticsrouter.post( '/category-analysis-card', validate( categoryAnalysisCardValid ), getCategoryAnalysisCard );
30
32
 
31
33
  // Conversation Analytics Routes
32
34
  audioAnalyticsrouter.post( '/conversations/list', validate( conversationsListValid ), getConversationsList );
@@ -77,10 +77,10 @@ export async function getConversationsListFromLambda( params ) {
77
77
  isAI: params.isAI,
78
78
  analyticsType: params.analyticsType,
79
79
  searchValue: params.searchValue,
80
+ audioSource: params.audioSource,
80
81
  limit: params.limit,
81
82
  offset: params.offset,
82
83
  };
83
- console.log( payload );
84
84
  const response = await axios.post( `${LAMBDA_ENDPOINT.cohortConversationList}/conversations/list`, payload, {
85
85
  timeout: 30000,
86
86
  } );
@@ -122,10 +122,10 @@ export async function getTranscriptListFromLambda( params ) {
122
122
  isAI: params.isAI,
123
123
  analyticsType: params.analyticsType,
124
124
  searchValue: params.searchValue,
125
+ audioSource: params.audioSource,
125
126
  limit: params.limit,
126
127
  offset: params.offset,
127
128
  };
128
- console.log( LAMBDA_ENDPOINT.transcriptList, payload );
129
129
  const response = await axios.post( `${LAMBDA_ENDPOINT.transcriptList}`, payload, {
130
130
  timeout: 30000,
131
131
  } );
@@ -289,6 +289,100 @@ export async function getCohortAnalysisSummaryCard( params ) {
289
289
  }
290
290
  }
291
291
 
292
+ /**
293
+ * Call Lambda to get category analysis card
294
+ * @param {Object} params - Request parameters
295
+ * @param {string} params.startDate - Start date (YYYY-MM-DD)
296
+ * @param {string} params.endDate - End date (YYYY-MM-DD)
297
+ * @param {string[]} params.storeId - Array of store IDs
298
+ * @param {string} params.cohortId - Cohort ID
299
+ * @param {string} params.clientId - Client ID
300
+ * @param {number} params.id - Category numeric ID
301
+ * @param {string} params.category - Category name
302
+ * @return {Promise<Object>} Response from Lambda
303
+ */
304
+ export async function getCategoryAnalysisCardFromLambda( params ) {
305
+ try {
306
+ const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
307
+
308
+ const payload = {
309
+ startDate: params.startDate,
310
+ endDate: params.endDate,
311
+ storeId: Array.isArray( params.storeId ) ? params.storeId : [ params.storeId ],
312
+ cohortId: params.cohortId,
313
+ clientId: params.clientId,
314
+ id: params.id,
315
+ category: params.category,
316
+ };
317
+
318
+ logger.info( { message: 'Calling Lambda for category analysis card', url: LAMBDA_ENDPOINT.categoryAnalysisCard, payload } );
319
+
320
+ const response = await axios.post( `${LAMBDA_ENDPOINT.categoryAnalysisCard}`, payload, {
321
+ headers: {
322
+ 'Content-Type': 'application/json',
323
+ },
324
+ timeout: 30000,
325
+ } );
326
+
327
+ logger.info( { message: 'Lambda response received for category analysis card', status: response.status } );
328
+ return response.data;
329
+ } catch ( error ) {
330
+ logger.error( {
331
+ error: error.message,
332
+ status: error.response?.status,
333
+ data: error.response?.data,
334
+ message: 'Error calling Lambda for category analysis card',
335
+ params,
336
+ } );
337
+ throw new Error( `Failed to fetch category analysis card: ${error.message}` );
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Call Lambda to get cohort summary card
343
+ * @param {Object} params - Request parameters
344
+ * @param {string} params.startDate - Start date (YYYY-MM-DD)
345
+ * @param {string} params.endDate - End date (YYYY-MM-DD)
346
+ * @param {string[]} params.storeId - Array of store IDs
347
+ * @param {string[]} params.clientId - Array of client IDs
348
+ * @param {string[]} params.cohortId - Array of cohort IDs
349
+ * @return {Promise<Object>} Response from Lambda
350
+ */
351
+ export async function getCohortSummaryCardFromLambda( params ) {
352
+ try {
353
+ const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
354
+
355
+ const payload = {
356
+ startDate: params.startDate,
357
+ endDate: params.endDate,
358
+ storeId: Array.isArray( params.storeId ) ? params.storeId : [ params.storeId ],
359
+ clientId: Array.isArray( params.clientId ) ? params.clientId : [ params.clientId ],
360
+ cohortId: Array.isArray( params.cohortId ) ? params.cohortId : [ params.cohortId ],
361
+ };
362
+
363
+ logger.info( { message: 'Calling Lambda for cohort summary card', url: LAMBDA_ENDPOINT.cohortSummaryCard, payload } );
364
+
365
+ const response = await axios.post( `${LAMBDA_ENDPOINT.cohortSummaryCard}`, payload, {
366
+ headers: {
367
+ 'Content-Type': 'application/json',
368
+ },
369
+ timeout: 30000,
370
+ } );
371
+
372
+ logger.info( { message: 'Lambda response received for cohort summary card', status: response.status } );
373
+ return response.data;
374
+ } catch ( error ) {
375
+ logger.error( {
376
+ error: error.message,
377
+ status: error.response?.status,
378
+ data: error.response?.data,
379
+ message: 'Error calling Lambda for cohort summary card',
380
+ params,
381
+ } );
382
+ throw new Error( `Failed to fetch cohort summary card: ${error.message}` );
383
+ }
384
+ }
385
+
292
386
  /**
293
387
  * Process and filter conversations based on search criteria
294
388
  * @param {Array} conversations - Array of conversation objects