-
Notifications
You must be signed in to change notification settings - Fork 5
/
response_builder.py
340 lines (276 loc) · 11.3 KB
/
response_builder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
from rest_framework import status
from rest_framework.response import Response
class ResponseBuilder:
version = '2.0'
VALIDATION = 'validation'
SKILL = 'skill'
def __init__(self, response_type: object) -> object:
"""
use status only when response_type is VALIDATION
:param response_type: use ResponseBuilder.VALIDATION or ResponseBuilder.SKILL
"""
if response_type not in [self.VALIDATION, self.SKILL]:
raise ValueError('Response_type must be ResponseBuilder.VALIDATION or ResponseBuilder.SKILL')
self.response_type = response_type
self.status = None
if response_type == self.SKILL:
self.response = {
'version': self.version,
'template': {
'outputs': []
}
}
def __set_status(self, status: str):
"""
Set self.status. Only Valid when response_type is VALIDATION.
:param status: str
:return: None
"""
if self.response_type != self.VALIDATION: raise ValueError(
'This cannot be called when response_type is not VALIDATION.')
if status not in ['FAIL', 'SUCCESS']:
raise ValueError('Status must be "FAIL" or "SUCCESS" when response_type is VALIDATION')
self.status = status
self.response = {
'status': status
}
def __add_outputs(self, data: dict):
"""
append data to self.response['template']['outputs']
:param data: dict
:return: None
"""
if self.response_type != self.SKILL:
raise ValueError('You cannot use this when response_type is ResponseBuilder.VALIDATION')
self.response.get('template').get('outputs').append(data)
print("response: ", self.response)
def __add_quick_reply(self, data: dict):
"""
append data to self.response['template']['quickReplies']
:param data: dict
:return: None
"""
if self.response_type != self.SKILL:
raise ValueError('You cannot use this when response_type is ResponseBuilder.VALIDATION')
quick_replies = self.response.get('template').get('quickReplies')
if quick_replies is None:
self.response.get('template')['quickReplies'] = []
self.response.get('template').get('quickReplies').append(data)
def __add_value(self, value):
"""
append value to self.response. only use for validation.
:type value: string or int
"""
if self.response_type != self.VALIDATION: raise ValueError(
'You cannot use __add_value() when response_type is ResponseBuilder.VALIDATION')
if self.status is None: raise ValueError('You should call set_status() first.')
if not (type(value) == int or type(value) == str): raise ValueError('value should be int or string.')
self.response['value'] = value
def __add_message(self, message):
"""
append message to self.response
:type message: string
"""
if self.response_type != self.VALIDATION: raise ValueError(
'You cannot use __add_message() when response_type is ResponseBuilder.VALIDATION')
if self.status is None: raise ValueError('You should call set_status() first.')
if not type(message) == str: raise ValueError('message should be string.')
self.response['message'] = message
def set_validation_success(self, value):
"""
Build response with value when validaion is successful.
:param value: string or int
:return: None
"""
self.__set_status('SUCCESS')
self.__add_value(value)
def set_validation_fail(self, value=None, message=None):
"""
Build response with value when validaion is failed.
:param value: string or int
:param message: str. Client will use it when failed.
:return: None
"""
self.__set_status('FAIL')
if value:
self.__add_value(value)
if message:
self.__add_message(message)
def add_simple_text(self, text: str):
"""
append simpleText(dict) with text to self.response['template']['outputs']
:param text: It can be up to 1,000 letters.
:return: None
(완성 예제)
{
"simpleText": {
"text": "간단한 텍스트 요소입니다."
}
}
"""
if not (type(text) == int or type(text) == str):
raise ValueError('message should be int or string.')
data = {
'simpleText': {
'text': text
}
}
self.__add_outputs(data=data)
def add_image(self, image_url: str, alt_text: str):
"""
append simpleImage(dict) with image_url and alt_text to self.response['template']['outputs']
:param image_url: URL
:param alt_text: It can be up to 1,000 letters.
:return: None
(완성 예제)
{
"simpleImage": {
"imageUrl": "http://k.kakaocdn.net/dn/83BvP/bl20duRC1Q1/lj3JUcmrzC53YIjNDkqbWK/i_6piz1p.jpg",
"altText": "보물상자입니다"
}
}
"""
if type(image_url) != str and 'http' not in image_url: raise ValueError('image_url should be string of URL.')
if type(alt_text) != str: raise ValueError('alt_text should be string')
data = {
'simpleImage': {
'imageUrl': image_url,
'altText': alt_text
}
}
self.__add_outputs(data=data)
def add_quick_reply(self, action: str, label: str, block_id: str = None, message_text: str = None):
"""
append quickReplies with action, label, or maybe block_id, message_text to self.response['template']['outputs']
:type action: string. "block" or "message"
:param label: string.
:param block_id: string. It is necessary when action is "block"
:param message_text: string.
:return: None
(완성 예제)
{
"action": "block",
"label": "예",
# "messageText": "예",
"blockId": "5da5eac292690d0001a4"
}
or
{
"action": "message",
"label": "아니요",
"messageText": "아니요"
}
"""
if action is None or label is None: raise ValueError('action or label is None.')
if action not in ['block', 'message']: raise ValueError('action should be block or message.')
if action == 'block' and block_id is None: raise ValueError('block_id is necessary when action is "block".')
if action == 'message' and message_text is None:
raise ValueError('message_text is recommended when action is "message".')
data = {
'action': action,
'label': label
}
if block_id:
data['blockId'] = block_id
if message_text:
data['messageText'] = message_text
self.__add_quick_reply(data=data)
def add_data(self, key: str, value: str):
"""
Add data field to response.
:param key: key must be string.
:param value: value must be JSON parsable.
:return: None
result will be like...
{
"version": "2.0",
"data": {
'nickname': patient.nickname
}
}
"""
if 'data' not in self.response:
self.response['data'] = {}
self.response['data'][key] = value
def set_quick_replies_yes_or_no(self, block_id_for_yes: str = None,
block_id_for_no: str = '5dc72a7492690d0001caebba',
message_text_for_yes: str = '예', message_text_for_no: str = '아니요, 종료할게요.'):
"""
Automaticaly add quick_replies for 예/아니요.
Currently you can only set block_id for yes, and message_text for no.
Use add_quick_reply() when you want other way.
:param block: bool
:param block_id_for_yes: str.
:param block_id_for_no: str. default is '5dc72a7492690d0001caebba' (취소 or 탈출 - 시작하기 처음으로)
:param message_text_for_yes: str. default is '예.'
:param message_text_for_no: str. default is '아니요, 종료할게요.'
:return: None
"""
if block_id_for_yes:
self.add_quick_reply(action='block', label='예', block_id=block_id_for_yes,
message_text=message_text_for_yes)
else:
self.add_quick_reply(action='message', label='예', block_id=block_id_for_yes,
message_text=message_text_for_yes)
if block_id_for_no:
self.add_quick_reply(action='block', label='아니요', block_id=block_id_for_no,
message_text=message_text_for_no)
else:
self.add_quick_reply(action='message', label='아니요', message_text=message_text_for_no)
def add_context(self, name: str, life_span: int = 5, params: dict = None):
"""
add context data
:param name: Name of Context. Camel case recommended.
:param life_span: default is 10.
:param params: dict of param's key and value. can include multiple parameters.
:return: None
ref) https://i.kakao.com/docs/skill-response-format#%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C-3
"""
if self.response_type != self.SKILL:
raise ValueError('You cannot use this when response_type is ResponseBuilder.VALIDATION')
if name is None or params is None:
raise ValueError('name or params is empty.')
value = {
"name": name,
"lifeSpan": life_span,
"params": params
}
try:
self.response['context']['value']
except KeyError:
self.response.__setitem__('context', {})
self.response['context'].__setitem__('values', [])
finally:
self.response['context']['values'].append(value)
def get_response(self):
"""
Return self.response with status_code 200
:return: dict. self.response
"""
return self.response
def get_response_200(self):
"""
Return Response with self.response and status_code(200)
:return: object. Response()
"""
return Response(self.response, status=status.HTTP_200_OK)
def get_response_400(self):
"""
Return Response with self.response and status_code(400)
:return: object. Response()
"""
return Response(self.response, status=status.HTTP_400_BAD_REQUEST)
def get_response_404(self):
"""
Return Response with self.response and status_code(404)
:return: object. Response()
"""
return Response(self.response, status=status.HTTP_404_NOT_FOUND)
@staticmethod
def get_response_200_without_data():
"""
Return Response without data.
It will be used in non-using skill data response, and non-returning any of parameter.
:return: object. Response()
"""
return Response(data={'version': '2.0'}, status=status.HTTP_200_OK)