intention.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. import logging
  2. from flask import request
  3. from flask_restful import Resource, marshal, marshal_with, reqparse
  4. from werkzeug.exceptions import Forbidden, NotFound
  5. from controllers.console import api
  6. from controllers.console.wraps import account_initialization_required, setup_required
  7. from fields.intention_fields import (
  8. intention_corpus_detail_fields,
  9. intention_corpus_similarity_question_fields,
  10. intention_detail_fields,
  11. intention_keyword_detail_fields,
  12. intention_keyword_fields,
  13. intention_page_fields,
  14. intention_type_detail_fields, intention_type_page_fields,
  15. )
  16. from libs.login import login_required
  17. from services.intention_service import (
  18. IntentionCorpusService,
  19. IntentionCorpusSimilarityQuestionService,
  20. IntentionKeywordService,
  21. IntentionService,
  22. IntentionTypeService,
  23. )
  24. class IntentionListApi(Resource):
  25. @setup_required
  26. @login_required
  27. @account_initialization_required
  28. def get(self):
  29. page = request.args.get("page", default=1, type=int)
  30. limit = request.args.get("limit", default=20, type=int)
  31. type_id = request.args.get("type_id", default=None, type=str)
  32. name_search = request.args.get("name_search", default=None, type=str)
  33. intentions, total = IntentionService.get_intentions(page, limit, type_id, name_search)
  34. data = marshal(intentions, intention_page_fields)
  35. response = {"data": data, "has_more": len(intentions) == limit, "limit": limit,
  36. "total": total, "page": page}
  37. return response, 200
  38. @setup_required
  39. @login_required
  40. @account_initialization_required
  41. def post(self):
  42. parser = reqparse.RequestParser()
  43. parser.add_argument(
  44. "name",
  45. nullable=False,
  46. required=True,
  47. help="type is required. Name must be between 1 to 40 characters.",
  48. )
  49. parser.add_argument(
  50. "type_id",
  51. nullable=False,
  52. required=True,
  53. help="type is required.",
  54. )
  55. args = parser.parse_args()
  56. intention = IntentionService.save_intention(args)
  57. response = marshal(intention, intention_detail_fields)
  58. return response, 200
  59. class IntentionApi(Resource):
  60. @setup_required
  61. @login_required
  62. @account_initialization_required
  63. def get(self, intention_id):
  64. intention = IntentionService.get_intention(intention_id)
  65. return marshal(intention, intention_detail_fields)
  66. @setup_required
  67. @login_required
  68. @account_initialization_required
  69. def patch(self, intention_id):
  70. parser = reqparse.RequestParser()
  71. parser.add_argument(
  72. "name",
  73. nullable=False,
  74. required=True,
  75. help="type is required. Name must be between 1 to 40 characters.",
  76. )
  77. parser.add_argument(
  78. "type_id",
  79. nullable=False,
  80. required=True,
  81. help="type is required.",
  82. )
  83. args = parser.parse_args()
  84. intention = IntentionService.update_intention(intention_id, args)
  85. response = marshal(intention, intention_detail_fields)
  86. return response, 200
  87. @setup_required
  88. @login_required
  89. @account_initialization_required
  90. def delete(self, intention_id):
  91. IntentionService.delete_intention(intention_id)
  92. return 200
  93. class IntentionTypeListApi(Resource):
  94. @setup_required
  95. @login_required
  96. @account_initialization_required
  97. def get(self):
  98. page = request.args.get("page", default=1, type=int)
  99. limit = request.args.get("limit", default=20, type=int)
  100. search = request.args.get("search", default=None, type=str)
  101. intention_types, total = IntentionTypeService.get_intention_types(page, limit, search)
  102. data = marshal(intention_types, intention_type_page_fields)
  103. response = {"data": data, "has_more": len(intention_types) == limit, "limit": limit,
  104. "total": total, "page": page}
  105. return response, 200
  106. @setup_required
  107. @login_required
  108. @account_initialization_required
  109. def post(self):
  110. parser = reqparse.RequestParser()
  111. parser.add_argument(
  112. "name",
  113. nullable=False,
  114. required=True,
  115. help="type is required. Name must be between 1 to 40 characters.",
  116. )
  117. args = parser.parse_args()
  118. intention_type = IntentionTypeService.save_intention_type(args)
  119. response = marshal(intention_type, intention_type_detail_fields)
  120. return response, 200
  121. class IntentionTypeApi(Resource):
  122. @setup_required
  123. @login_required
  124. @account_initialization_required
  125. def get(self, intention_type_id):
  126. intention_type = IntentionTypeService.get_intention_type(intention_type_id)
  127. return marshal(intention_type, intention_type_detail_fields)
  128. @setup_required
  129. @login_required
  130. @account_initialization_required
  131. def patch(self, intention_type_id):
  132. parser = reqparse.RequestParser()
  133. parser.add_argument(
  134. "name",
  135. nullable=False,
  136. required=True,
  137. help="type is required. Name must be between 1 to 40 characters.",
  138. )
  139. args = parser.parse_args()
  140. intention_type = IntentionTypeService.update_intention_type(intention_type_id, args)
  141. return marshal(intention_type, intention_type_detail_fields), 200
  142. @setup_required
  143. @login_required
  144. @account_initialization_required
  145. def delete(self, intention_type_id):
  146. IntentionTypeService.delete_intention_type(intention_type_id)
  147. return 200
  148. class IntentionKeywordListApi(Resource):
  149. @setup_required
  150. @login_required
  151. @account_initialization_required
  152. @marshal_with(intention_keyword_fields)
  153. def get(self, intention_id):
  154. search = request.args.get("search", default=None, type=str)
  155. intention = IntentionService.get_intention(intention_id)
  156. if not intention:
  157. raise NotFound("Intention not found")
  158. intention_keywords = IntentionKeywordService.get_intention_keywords(intention_id, search)
  159. return intention_keywords, 200
  160. @setup_required
  161. @login_required
  162. @account_initialization_required
  163. @marshal_with(intention_keyword_detail_fields)
  164. def post(self, intention_id):
  165. parser = reqparse.RequestParser()
  166. parser.add_argument(
  167. "name",
  168. nullable=False,
  169. required=True,
  170. help="type is required. Name must be between 1 to 40 characters.",
  171. )
  172. args = parser.parse_args()
  173. intention = IntentionService.get_intention(intention_id)
  174. if not intention:
  175. raise NotFound("Intention not found")
  176. intention_keyword = IntentionKeywordService.save_intention_keyword(intention_id, args)
  177. return intention_keyword, 200
  178. @setup_required
  179. @login_required
  180. @account_initialization_required
  181. def delete(self, intention_id):
  182. intention = IntentionService.get_intention(intention_id)
  183. if not intention:
  184. raise NotFound("Intention not found")
  185. IntentionKeywordService.delete_intention_keywords_by_intention_id(intention_id)
  186. return 200
  187. class IntentionKeywordApi(Resource):
  188. @setup_required
  189. @login_required
  190. @account_initialization_required
  191. def get(self, intention_keyword_id):
  192. intention_keyword = IntentionKeywordService.get_intention_keyword(intention_keyword_id)
  193. if not intention_keyword:
  194. return {}, 200
  195. return marshal(intention_keyword, intention_keyword_detail_fields), 200
  196. @setup_required
  197. @login_required
  198. @account_initialization_required
  199. @marshal_with(intention_keyword_detail_fields)
  200. def patch(self, intention_keyword_id):
  201. parser = reqparse.RequestParser()
  202. parser.add_argument(
  203. "name",
  204. nullable=False,
  205. required=True,
  206. help="type is required. Name must be between 1 to 40 characters.",
  207. )
  208. parser.add_argument(
  209. "intention_id",
  210. nullable=False,
  211. required=True,
  212. help="type is required.",
  213. )
  214. args = parser.parse_args()
  215. intention_keyword = IntentionKeywordService.update_intention_keyword(intention_keyword_id, args)
  216. return intention_keyword, 200
  217. @setup_required
  218. @login_required
  219. @account_initialization_required
  220. def delete(self, intention_keyword_id):
  221. IntentionKeywordService.delete_intention_keyword(intention_keyword_id)
  222. return 200
  223. class IntentionKeywordBatchApi(Resource):
  224. @setup_required
  225. @login_required
  226. @account_initialization_required
  227. def post(self):
  228. parser = reqparse.RequestParser()
  229. parser.add_argument(
  230. "method",
  231. nullable=False,
  232. required=True,
  233. help="method is required.",
  234. choices=["create", "update", "delete"],
  235. type=str,
  236. location="json",
  237. )
  238. parser.add_argument(
  239. "delete_data",
  240. nullable=False,
  241. required=True,
  242. help="delete_data is required.",
  243. type=list,
  244. location="json",
  245. )
  246. args = parser.parse_args()
  247. logging.info(args)
  248. method = args["method"]
  249. if method == "delete":
  250. intention_keyword_ids = args["delete_data"]
  251. IntentionKeywordService.delete_intention_keywords(intention_keyword_ids)
  252. return 200
  253. else:
  254. raise NotFound(f"method with name {method} not found")
  255. class IntentionCorpusListApi(Resource):
  256. @setup_required
  257. @login_required
  258. @account_initialization_required
  259. def get(self):
  260. page = request.args.get("page", default=1, type=int)
  261. limit = request.args.get("limit", default=20, type=int)
  262. question_search = request.args.get("question_search", default=None, type=str)
  263. intention_id = request.args.get("intention_id", default=None, type=str)
  264. intention_corpus, total = IntentionCorpusService.get_page_intention_corpus(
  265. page, limit, question_search, intention_id)
  266. data = marshal(intention_corpus, intention_corpus_detail_fields)
  267. response = {"data": data, "has_more": len(intention_corpus) == limit, "limit": limit,
  268. "total": total, "page": page}
  269. return response, 200
  270. @setup_required
  271. @login_required
  272. @account_initialization_required
  273. def post(self):
  274. parser = reqparse.RequestParser()
  275. parser.add_argument(
  276. "question",
  277. nullable=False,
  278. required=True,
  279. help="type is required. Question must be between 1 to 40 characters.",
  280. )
  281. parser.add_argument(
  282. "question_config",
  283. nullable=True,
  284. required=False,
  285. location="json",
  286. )
  287. parser.add_argument(
  288. "intention_id",
  289. nullable=False,
  290. required=True,
  291. help="type is required.",
  292. )
  293. args = parser.parse_args()
  294. intention_corpus = IntentionCorpusService.save_intention_corpus(args)
  295. return marshal(intention_corpus, intention_corpus_detail_fields), 200
  296. class IntentionCorpusApi(Resource):
  297. @setup_required
  298. @login_required
  299. @account_initialization_required
  300. def get(self, corpus_id):
  301. intention_corpus = IntentionCorpusService.get_intention_corpus(corpus_id)
  302. if not intention_corpus:
  303. raise NotFound(f"IntentionCorpus with id {corpus_id} not found")
  304. return marshal(intention_corpus, intention_corpus_detail_fields), 200
  305. @setup_required
  306. @login_required
  307. @account_initialization_required
  308. def patch(self, corpus_id):
  309. parser = reqparse.RequestParser()
  310. parser.add_argument(
  311. "question",
  312. nullable=True,
  313. required=False,
  314. type=str,
  315. location="json",
  316. )
  317. parser.add_argument(
  318. "question_config",
  319. nullable=True,
  320. required=False,
  321. location="json",
  322. )
  323. parser.add_argument(
  324. "intention_id",
  325. nullable=True,
  326. required=False,
  327. type=str,
  328. location="json",
  329. )
  330. args = parser.parse_args()
  331. intention_corpus = IntentionCorpusService.update_intention_corpus(corpus_id, args)
  332. return marshal(intention_corpus, intention_corpus_detail_fields), 200
  333. @setup_required
  334. @login_required
  335. @account_initialization_required
  336. def delete(self, corpus_id):
  337. intention_corpus = IntentionCorpusService.get_intention_corpus(corpus_id)
  338. if not intention_corpus:
  339. raise NotFound(f"未发现Id未{corpus_id}的训练语料")
  340. similarity_questions = intention_corpus.similarity_questions
  341. if similarity_questions:
  342. raise Forbidden(f"存在与其关联的相似问题,无法删除Id为{corpus_id}训练语料")
  343. IntentionCorpusService.delete_intention_corpus(intention_corpus)
  344. return 200
  345. class IntentionCorpusSimilarityQuestionApi(Resource):
  346. @setup_required
  347. @login_required
  348. @account_initialization_required
  349. def get(self, corpus_id):
  350. search = request.args.get("search", default=None, type=str)
  351. similarity_questions = (
  352. IntentionCorpusSimilarityQuestionService
  353. .get_similarity_questions_by_corpus_id_like_question(corpus_id, search)
  354. )
  355. return marshal(similarity_questions, intention_corpus_similarity_question_fields), 200
  356. @setup_required
  357. @login_required
  358. @account_initialization_required
  359. def post(self, corpus_id):
  360. parser = reqparse.RequestParser()
  361. parser.add_argument(
  362. "question",
  363. nullable=False,
  364. required=True,
  365. help="type is required. Question must be between 1 to 40 characters.",
  366. location="json",
  367. )
  368. parser.add_argument(
  369. "question_config",
  370. nullable=True,
  371. required=False,
  372. location="json",
  373. )
  374. args = parser.parse_args()
  375. intention_corpus_similarity_question = (
  376. IntentionCorpusSimilarityQuestionService.save_similarity_question(corpus_id, args)
  377. )
  378. return marshal(intention_corpus_similarity_question, intention_corpus_similarity_question_fields), 200
  379. @setup_required
  380. @login_required
  381. @account_initialization_required
  382. def delete(self, corpus_id):
  383. IntentionCorpusSimilarityQuestionService.delete_similarity_question_by_corpus_id(corpus_id)
  384. return 200
  385. class IntentionCorpusSimilarityQuestionUpdateAndDeleteApi(Resource):
  386. @setup_required
  387. @login_required
  388. @account_initialization_required
  389. def patch(self, similarity_question_id):
  390. parser = reqparse.RequestParser()
  391. parser.add_argument(
  392. "question",
  393. nullable=True,
  394. required=False,
  395. help="type is required. Question must be between 1 to 40 characters.",
  396. location="json",
  397. )
  398. parser.add_argument(
  399. "question_config",
  400. nullable=True,
  401. required=False,
  402. location="json",
  403. )
  404. parser.add_argument(
  405. "corpus_id",
  406. nullable=True,
  407. required=False,
  408. location="json",
  409. )
  410. args = parser.parse_args()
  411. similarity_question = (
  412. IntentionCorpusSimilarityQuestionService.update_similarity_question(similarity_question_id, args)
  413. )
  414. return marshal(similarity_question, intention_corpus_similarity_question_fields), 200
  415. @setup_required
  416. @login_required
  417. @account_initialization_required
  418. def delete(self, similarity_question_id):
  419. IntentionCorpusSimilarityQuestionService.delete_similarity_question_by_id(similarity_question_id)
  420. return 200
  421. class IntentionCorpusSimilarityQuestionBatchApi(Resource):
  422. @setup_required
  423. @login_required
  424. @account_initialization_required
  425. def post(self):
  426. parser = reqparse.RequestParser()
  427. parser.add_argument(
  428. "method",
  429. nullable=False,
  430. required=True,
  431. help="method is required.",
  432. choices=["create", "update", "delete"],
  433. type=str,
  434. location="json",
  435. )
  436. parser.add_argument(
  437. "data",
  438. nullable=False,
  439. required=True,
  440. help="data is required.",
  441. type=list,
  442. location="json",
  443. )
  444. args = parser.parse_args()
  445. logging.info(args)
  446. method = args["method"]
  447. if method == "delete":
  448. similarity_question_ids = args["data"]
  449. IntentionCorpusSimilarityQuestionService.delete_similarity_questions_by_ids(similarity_question_ids)
  450. return 200
  451. else:
  452. raise NotFound(f"method with name {method} not found")
  453. api.add_resource(IntentionListApi, "/intentions")
  454. api.add_resource(IntentionApi, "/intentions/<uuid:intention_id>")
  455. api.add_resource(IntentionTypeListApi, "/intentions/types")
  456. api.add_resource(IntentionTypeApi, "/intentions/types/<uuid:intention_type_id>")
  457. api.add_resource(IntentionKeywordListApi, "/intentions/<uuid:intention_id>/keywords")
  458. api.add_resource(IntentionKeywordApi, "/intentions/keywords/<uuid:intention_keyword_id>")
  459. api.add_resource(IntentionKeywordBatchApi, "/intentions/keywords/batch")
  460. api.add_resource(IntentionCorpusListApi, "/intentions/corpus")
  461. api.add_resource(IntentionCorpusApi, "/intentions/corpus/<uuid:corpus_id>")
  462. api.add_resource(IntentionCorpusSimilarityQuestionApi, "/intentions/corpus/<uuid:corpus_id>/similarity_questions")
  463. api.add_resource(IntentionCorpusSimilarityQuestionUpdateAndDeleteApi,
  464. "/intentions/similarity_questions/<uuid:similarity_question_id>")
  465. api.add_resource(IntentionCorpusSimilarityQuestionBatchApi, "/intentions/similarity_questions/batch")