tango-app-api-audio-analytics 1.0.24 → 1.0.25
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/COHORT_API_EXAMPLES.sh +235 -235
- package/package.json +1 -1
- package/src/controllers/audioAnalytics.controller.js +0 -1
- package/src/controllers/conversationAnalytics.controller.js +109 -0
- package/src/dtos/audioAnalytics.dtos.js +17 -0
- package/src/routes/audioAnalytics.routes.js +3 -2
- package/src/services/conversation.service.js +45 -0
package/COHORT_API_EXAMPLES.sh
CHANGED
|
@@ -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
|
@@ -571,7 +571,6 @@ export async function aiStreamResponse( req, res ) {
|
|
|
571
571
|
},
|
|
572
572
|
body: JSON.stringify( payload ),
|
|
573
573
|
} );
|
|
574
|
-
logger.info( { message: 'Called external stream API', status: response } );
|
|
575
574
|
if ( !response.ok ) {
|
|
576
575
|
logger.error( response, `External Chat Stream API error: ${response.status} ${response.statusText}` );
|
|
577
576
|
return res.sendError( `External API error: ${response.statusText}`, response.status );
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
getConversationsListFromLambda,
|
|
5
5
|
exportConversationsFromLambda,
|
|
6
6
|
getConversationDetailsFromLambda,
|
|
7
|
+
getTranscriptListFromLambda,
|
|
7
8
|
// filterConversationsBySearch,
|
|
8
9
|
// sortConversations,
|
|
9
10
|
} from '../services/conversation.service.js';
|
|
@@ -158,7 +159,115 @@ export const getConversationsList = async ( req, res ) => {
|
|
|
158
159
|
} );
|
|
159
160
|
}
|
|
160
161
|
};
|
|
162
|
+
/**
|
|
163
|
+
* Get Transcript Analysis List
|
|
164
|
+
* POST /Transcripts/list
|
|
165
|
+
* @param {Object} req - Express request object
|
|
166
|
+
* @param {Object} req.body - Request body with query parameters
|
|
167
|
+
* @param {string} req.body.startDate - Start date for filtering
|
|
168
|
+
* @param {string} req.body.endDate - End date for filtering
|
|
169
|
+
* @param {string[]} req.body.storeId - Array of store IDs for filtering
|
|
170
|
+
* @param {string[]} req.body.clientId - Optional array of client IDs
|
|
171
|
+
* @param {boolean} req.body.isAI - Filter for AI Transcripts
|
|
172
|
+
* @param {string} req.body.analyticsType - Type of analytics (audio, text, all)
|
|
173
|
+
* @param {string} req.body.searchValue - Search term for filtering
|
|
174
|
+
* @param {number} req.body.limit - Pagination limit
|
|
175
|
+
* @param {number} req.body.offset - Pagination offset
|
|
176
|
+
* @param {boolean} req.body.isExport - Flag to export as CSV
|
|
177
|
+
* @param {Object} res - Express response object
|
|
178
|
+
* @return {void} Returns JSON response with Transcripts list or CSV file
|
|
179
|
+
*/
|
|
180
|
+
export const getTranscriptsList = async ( req, res ) => {
|
|
181
|
+
try {
|
|
182
|
+
const {
|
|
183
|
+
startDate,
|
|
184
|
+
endDate,
|
|
185
|
+
storeId,
|
|
186
|
+
clientId,
|
|
187
|
+
cohortType,
|
|
188
|
+
isAI,
|
|
189
|
+
analyticsType,
|
|
190
|
+
searchValue,
|
|
191
|
+
limit,
|
|
192
|
+
offset,
|
|
193
|
+
isExport,
|
|
194
|
+
_id,
|
|
195
|
+
name,
|
|
196
|
+
} = req.body;
|
|
197
|
+
|
|
198
|
+
// Validation
|
|
199
|
+
if ( !startDate || !endDate || !storeId ) {
|
|
200
|
+
return res.status( 400 ).json( {
|
|
201
|
+
status: 'error',
|
|
202
|
+
message: 'Missing required parameters: startDate, endDate, storeId',
|
|
203
|
+
code: 'MISSING_PARAMETERS',
|
|
204
|
+
timestamp: new Date().toISOString(),
|
|
205
|
+
} );
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
logger.info( {
|
|
209
|
+
message: 'Fetching Transcripts list',
|
|
210
|
+
startDate,
|
|
211
|
+
endDate,
|
|
212
|
+
storeId,
|
|
213
|
+
isExport,
|
|
214
|
+
} );
|
|
161
215
|
|
|
216
|
+
// let Transcripts;
|
|
217
|
+
// let totalCount;
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
// Call Lambda to get Transcripts (non-export)
|
|
221
|
+
const lambdaResponse = await getTranscriptListFromLambda( {
|
|
222
|
+
startDate,
|
|
223
|
+
endDate,
|
|
224
|
+
storeId,
|
|
225
|
+
clientId,
|
|
226
|
+
cohortType,
|
|
227
|
+
isAI,
|
|
228
|
+
analyticsType,
|
|
229
|
+
searchValue,
|
|
230
|
+
limit,
|
|
231
|
+
offset,
|
|
232
|
+
_id,
|
|
233
|
+
name,
|
|
234
|
+
} );
|
|
235
|
+
// Transcripts = lambdaResponse.Transcripts || [];
|
|
236
|
+
// totalCount = lambdaResponse.totalCount || Transcripts.length;
|
|
237
|
+
|
|
238
|
+
// // Apply search filter if provided
|
|
239
|
+
// if ( searchValue ) {
|
|
240
|
+
// Transcripts = filterTranscriptsBySearch( Transcripts, searchValue );
|
|
241
|
+
// }
|
|
242
|
+
|
|
243
|
+
// // Apply sorting
|
|
244
|
+
// Transcripts = sortTranscripts( Transcripts, 'date', 'desc' );
|
|
245
|
+
|
|
246
|
+
// Return paginated JSON response
|
|
247
|
+
return res.status( 200 ).json( {
|
|
248
|
+
status: 'success',
|
|
249
|
+
data: lambdaResponse,
|
|
250
|
+
// {
|
|
251
|
+
// result: lambdaResponse,
|
|
252
|
+
// pagination: {
|
|
253
|
+
// limit,
|
|
254
|
+
// offset,
|
|
255
|
+
// total: totalCount,
|
|
256
|
+
// hasMore: ( offset + limit ) < totalCount,
|
|
257
|
+
// },
|
|
258
|
+
// },
|
|
259
|
+
timestamp: new Date().toISOString(),
|
|
260
|
+
} );
|
|
261
|
+
} catch ( error ) {
|
|
262
|
+
logger.error( { error, message: 'Error fetching Transcripts list', body: req.body } );
|
|
263
|
+
return res.status( 500 ).json( {
|
|
264
|
+
status: 'error',
|
|
265
|
+
message: error.message || 'Internal server error',
|
|
266
|
+
code: 'INTERNAL_ERROR',
|
|
267
|
+
timestamp: new Date().toISOString(),
|
|
268
|
+
} );
|
|
269
|
+
}
|
|
270
|
+
};
|
|
162
271
|
/**
|
|
163
272
|
* Get Single Conversation Details
|
|
164
273
|
* POST /conversations/:conversationId
|
|
@@ -527,6 +527,23 @@ export const conversationsListValid = joi.object( {
|
|
|
527
527
|
offset: joi.number().integer().min( 0 ).optional().default( 0 ).description( 'Pagination offset' ),
|
|
528
528
|
} ).strict();
|
|
529
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Transcript List Schema
|
|
532
|
+
*/
|
|
533
|
+
export const transcriptListValid = joi.object( {
|
|
534
|
+
startDate: joi.string().required().pattern( /^\d{4}-\d{2}-\d{2}$/ ).description( 'Start date in YYYY-MM-DD format' ),
|
|
535
|
+
endDate: joi.string().required().pattern( /^\d{4}-\d{2}-\d{2}$/ ).description( 'End date in YYYY-MM-DD format' ),
|
|
536
|
+
storeId: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of store IDs' ),
|
|
537
|
+
clientId: joi.array().items( joi.string() ).optional().description( 'Array of client IDs' ),
|
|
538
|
+
cohortType: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of cohort types' ),
|
|
539
|
+
isAI: joi.boolean().optional().description( 'Filter for AI-generated conversations' ),
|
|
540
|
+
analyticsType: joi.string().optional().valid( 'audio', 'text', 'all' ).description( 'Type of analytics' ),
|
|
541
|
+
searchValue: joi.string().optional().description( 'Search term' ),
|
|
542
|
+
isExport: joi.boolean().optional().description( 'Flag to export as CSV' ),
|
|
543
|
+
limit: joi.number().integer().min( 1 ).max( 1000 ).optional().default( 10 ).description( 'Pagination limit' ),
|
|
544
|
+
offset: joi.number().integer().min( 0 ).optional().default( 0 ).description( 'Pagination offset' ),
|
|
545
|
+
} ).strict();
|
|
546
|
+
|
|
530
547
|
/**
|
|
531
548
|
* Conversation Details Schema
|
|
532
549
|
*/
|
|
@@ -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 } from '../dtos/audioAnalytics.dtos.js';
|
|
5
|
+
import { cohortAnalysisCardValid, 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
7
|
import { getCohortAnalysisCard } from '../controllers/cohortAnalytics.controller.js';
|
|
8
|
-
import { getConversationsList, getConversationDetails } from '../controllers/conversationAnalytics.controller.js';
|
|
8
|
+
import { getConversationsList, getTranscriptsList, getConversationDetails } from '../controllers/conversationAnalytics.controller.js';
|
|
9
9
|
|
|
10
10
|
export const audioAnalyticsrouter = express.Router(); ;
|
|
11
11
|
|
|
@@ -30,6 +30,7 @@ audioAnalyticsrouter.post( '/cohort-analysis-card', validate( cohortAnalysisCard
|
|
|
30
30
|
|
|
31
31
|
// Conversation Analytics Routes
|
|
32
32
|
audioAnalyticsrouter.post( '/conversations/list', validate( conversationsListValid ), getConversationsList );
|
|
33
|
+
audioAnalyticsrouter.post( '/transcript/list', validate( transcriptListValid ), getTranscriptsList );
|
|
33
34
|
audioAnalyticsrouter.post( '/conversations/:conversationId', validate( conversationDetailsValid ), getConversationDetails );
|
|
34
35
|
|
|
35
36
|
|
|
@@ -93,6 +93,51 @@ export async function getConversationsListFromLambda( params ) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Call Lambda to get transcript list
|
|
98
|
+
* @param {Object} params - Request parameters
|
|
99
|
+
* @param {string} params.startDate - Start date (YYYY-MM-DD)
|
|
100
|
+
* @param {string} params.endDate - End date (YYYY-MM-DD)
|
|
101
|
+
* @param {string[]} params.storeId - Array of store IDs
|
|
102
|
+
* @param {string[]} params.clientId - Array of client IDs
|
|
103
|
+
* @param {boolean} params.isAI - Filter for AI conversations
|
|
104
|
+
* @param {string} params.analyticsType - Type of analytics
|
|
105
|
+
* @param {string} params.searchValue - Search term
|
|
106
|
+
* @param {number} params.limit - Pagination limit
|
|
107
|
+
* @param {number} params.offset - Pagination offset
|
|
108
|
+
* @return {Promise<Object>} Response from Lambda
|
|
109
|
+
*/
|
|
110
|
+
export async function getTranscriptListFromLambda( params ) {
|
|
111
|
+
try {
|
|
112
|
+
const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
|
|
113
|
+
logger.info( { message: 'Calling Lambda for transcript list', params } );
|
|
114
|
+
const payload = {
|
|
115
|
+
startDate: params.startDate,
|
|
116
|
+
endDate: params.endDate,
|
|
117
|
+
storeId: params.storeId,
|
|
118
|
+
clientId: params.clientId,
|
|
119
|
+
cohort_id: params?.cohortType?.[0],
|
|
120
|
+
_id: params?._id,
|
|
121
|
+
name: params?.name,
|
|
122
|
+
isAI: params.isAI,
|
|
123
|
+
analyticsType: params.analyticsType,
|
|
124
|
+
searchValue: params.searchValue,
|
|
125
|
+
limit: params.limit,
|
|
126
|
+
offset: params.offset,
|
|
127
|
+
};
|
|
128
|
+
console.log( LAMBDA_ENDPOINT.transcriptList, payload );
|
|
129
|
+
const response = await axios.post( `${LAMBDA_ENDPOINT.transcriptList}`, payload, {
|
|
130
|
+
timeout: 30000,
|
|
131
|
+
} );
|
|
132
|
+
|
|
133
|
+
logger.info( { message: 'Lambda response received for transcript list', payload, totalCount: response } );
|
|
134
|
+
return response.data;
|
|
135
|
+
} catch ( error ) {
|
|
136
|
+
logger.error( { error, message: 'Error calling Lambda for transcript list', params } );
|
|
137
|
+
throw new Error( `Failed to fetch transcript list: ${error.message}` );
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
96
141
|
/**
|
|
97
142
|
* Call Lambda to get conversations for export
|
|
98
143
|
* @param {Object} params - Request parameters
|