statistic.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. # -*- coding:utf-8 -*-
  2. from decimal import Decimal
  3. from datetime import datetime
  4. import pytz
  5. from flask import jsonify
  6. from flask_login import login_required, current_user
  7. from flask_restful import Resource, reqparse
  8. from controllers.console import api
  9. from controllers.console.app import _get_app
  10. from controllers.console.setup import setup_required
  11. from controllers.console.wraps import account_initialization_required
  12. from libs.helper import datetime_string
  13. from extensions.ext_database import db
  14. class DailyConversationStatistic(Resource):
  15. @setup_required
  16. @login_required
  17. @account_initialization_required
  18. def get(self, app_id):
  19. account = current_user
  20. app_id = str(app_id)
  21. app_model = _get_app(app_id)
  22. parser = reqparse.RequestParser()
  23. parser.add_argument('start', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  24. parser.add_argument('end', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  25. args = parser.parse_args()
  26. sql_query = '''
  27. SELECT date(DATE_TRUNC('day', created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date, count(distinct messages.conversation_id) AS conversation_count
  28. FROM messages where app_id = :app_id
  29. '''
  30. arg_dict = {'tz': account.timezone, 'app_id': app_model.id}
  31. timezone = pytz.timezone(account.timezone)
  32. utc_timezone = pytz.utc
  33. if args['start']:
  34. start_datetime = datetime.strptime(args['start'], '%Y-%m-%d %H:%M')
  35. start_datetime = start_datetime.replace(second=0)
  36. start_datetime_timezone = timezone.localize(start_datetime)
  37. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  38. sql_query += ' and created_at >= :start'
  39. arg_dict['start'] = start_datetime_utc
  40. if args['end']:
  41. end_datetime = datetime.strptime(args['end'], '%Y-%m-%d %H:%M')
  42. end_datetime = end_datetime.replace(second=0)
  43. end_datetime_timezone = timezone.localize(end_datetime)
  44. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  45. sql_query += ' and created_at < :end'
  46. arg_dict['end'] = end_datetime_utc
  47. sql_query += ' GROUP BY date order by date'
  48. with db.engine.begin() as conn:
  49. rs = conn.execute(db.text(sql_query), arg_dict)
  50. response_data = []
  51. for i in rs:
  52. response_data.append({
  53. 'date': str(i.date),
  54. 'conversation_count': i.conversation_count
  55. })
  56. return jsonify({
  57. 'data': response_data
  58. })
  59. class DailyTerminalsStatistic(Resource):
  60. @setup_required
  61. @login_required
  62. @account_initialization_required
  63. def get(self, app_id):
  64. account = current_user
  65. app_id = str(app_id)
  66. app_model = _get_app(app_id)
  67. parser = reqparse.RequestParser()
  68. parser.add_argument('start', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  69. parser.add_argument('end', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  70. args = parser.parse_args()
  71. sql_query = '''
  72. SELECT date(DATE_TRUNC('day', created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date, count(distinct messages.from_end_user_id) AS terminal_count
  73. FROM messages where app_id = :app_id
  74. '''
  75. arg_dict = {'tz': account.timezone, 'app_id': app_model.id}
  76. timezone = pytz.timezone(account.timezone)
  77. utc_timezone = pytz.utc
  78. if args['start']:
  79. start_datetime = datetime.strptime(args['start'], '%Y-%m-%d %H:%M')
  80. start_datetime = start_datetime.replace(second=0)
  81. start_datetime_timezone = timezone.localize(start_datetime)
  82. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  83. sql_query += ' and created_at >= :start'
  84. arg_dict['start'] = start_datetime_utc
  85. if args['end']:
  86. end_datetime = datetime.strptime(args['end'], '%Y-%m-%d %H:%M')
  87. end_datetime = end_datetime.replace(second=0)
  88. end_datetime_timezone = timezone.localize(end_datetime)
  89. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  90. sql_query += ' and created_at < :end'
  91. arg_dict['end'] = end_datetime_utc
  92. sql_query += ' GROUP BY date order by date'
  93. with db.engine.begin() as conn:
  94. rs = conn.execute(db.text(sql_query), arg_dict)
  95. response_data = []
  96. for i in rs:
  97. response_data.append({
  98. 'date': str(i.date),
  99. 'terminal_count': i.terminal_count
  100. })
  101. return jsonify({
  102. 'data': response_data
  103. })
  104. class DailyTokenCostStatistic(Resource):
  105. @setup_required
  106. @login_required
  107. @account_initialization_required
  108. def get(self, app_id):
  109. account = current_user
  110. app_id = str(app_id)
  111. app_model = _get_app(app_id)
  112. parser = reqparse.RequestParser()
  113. parser.add_argument('start', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  114. parser.add_argument('end', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  115. args = parser.parse_args()
  116. sql_query = '''
  117. SELECT date(DATE_TRUNC('day', created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  118. (sum(messages.message_tokens) + sum(messages.answer_tokens)) as token_count,
  119. sum(total_price) as total_price
  120. FROM messages where app_id = :app_id
  121. '''
  122. arg_dict = {'tz': account.timezone, 'app_id': app_model.id}
  123. timezone = pytz.timezone(account.timezone)
  124. utc_timezone = pytz.utc
  125. if args['start']:
  126. start_datetime = datetime.strptime(args['start'], '%Y-%m-%d %H:%M')
  127. start_datetime = start_datetime.replace(second=0)
  128. start_datetime_timezone = timezone.localize(start_datetime)
  129. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  130. sql_query += ' and created_at >= :start'
  131. arg_dict['start'] = start_datetime_utc
  132. if args['end']:
  133. end_datetime = datetime.strptime(args['end'], '%Y-%m-%d %H:%M')
  134. end_datetime = end_datetime.replace(second=0)
  135. end_datetime_timezone = timezone.localize(end_datetime)
  136. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  137. sql_query += ' and created_at < :end'
  138. arg_dict['end'] = end_datetime_utc
  139. sql_query += ' GROUP BY date order by date'
  140. with db.engine.begin() as conn:
  141. rs = conn.execute(db.text(sql_query), arg_dict)
  142. response_data = []
  143. for i in rs:
  144. response_data.append({
  145. 'date': str(i.date),
  146. 'token_count': i.token_count,
  147. 'total_price': i.total_price,
  148. 'currency': 'USD'
  149. })
  150. return jsonify({
  151. 'data': response_data
  152. })
  153. class AverageSessionInteractionStatistic(Resource):
  154. @setup_required
  155. @login_required
  156. @account_initialization_required
  157. def get(self, app_id):
  158. account = current_user
  159. app_id = str(app_id)
  160. app_model = _get_app(app_id, 'chat')
  161. parser = reqparse.RequestParser()
  162. parser.add_argument('start', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  163. parser.add_argument('end', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  164. args = parser.parse_args()
  165. sql_query = """SELECT date(DATE_TRUNC('day', c.created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  166. AVG(subquery.message_count) AS interactions
  167. FROM (SELECT m.conversation_id, COUNT(m.id) AS message_count
  168. FROM conversations c
  169. JOIN messages m ON c.id = m.conversation_id
  170. WHERE c.override_model_configs IS NULL AND c.app_id = :app_id"""
  171. arg_dict = {'tz': account.timezone, 'app_id': app_model.id}
  172. timezone = pytz.timezone(account.timezone)
  173. utc_timezone = pytz.utc
  174. if args['start']:
  175. start_datetime = datetime.strptime(args['start'], '%Y-%m-%d %H:%M')
  176. start_datetime = start_datetime.replace(second=0)
  177. start_datetime_timezone = timezone.localize(start_datetime)
  178. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  179. sql_query += ' and c.created_at >= :start'
  180. arg_dict['start'] = start_datetime_utc
  181. if args['end']:
  182. end_datetime = datetime.strptime(args['end'], '%Y-%m-%d %H:%M')
  183. end_datetime = end_datetime.replace(second=0)
  184. end_datetime_timezone = timezone.localize(end_datetime)
  185. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  186. sql_query += ' and c.created_at < :end'
  187. arg_dict['end'] = end_datetime_utc
  188. sql_query += """
  189. GROUP BY m.conversation_id) subquery
  190. LEFT JOIN conversations c on c.id=subquery.conversation_id
  191. GROUP BY date
  192. ORDER BY date"""
  193. with db.engine.begin() as conn:
  194. rs = conn.execute(db.text(sql_query), arg_dict)
  195. response_data = []
  196. for i in rs:
  197. response_data.append({
  198. 'date': str(i.date),
  199. 'interactions': float(i.interactions.quantize(Decimal('0.01')))
  200. })
  201. return jsonify({
  202. 'data': response_data
  203. })
  204. class UserSatisfactionRateStatistic(Resource):
  205. @setup_required
  206. @login_required
  207. @account_initialization_required
  208. def get(self, app_id):
  209. account = current_user
  210. app_id = str(app_id)
  211. app_model = _get_app(app_id)
  212. parser = reqparse.RequestParser()
  213. parser.add_argument('start', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  214. parser.add_argument('end', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  215. args = parser.parse_args()
  216. sql_query = '''
  217. SELECT date(DATE_TRUNC('day', m.created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  218. COUNT(m.id) as message_count, COUNT(mf.id) as feedback_count
  219. FROM messages m
  220. LEFT JOIN message_feedbacks mf on mf.message_id=m.id
  221. WHERE m.app_id = :app_id
  222. '''
  223. arg_dict = {'tz': account.timezone, 'app_id': app_model.id}
  224. timezone = pytz.timezone(account.timezone)
  225. utc_timezone = pytz.utc
  226. if args['start']:
  227. start_datetime = datetime.strptime(args['start'], '%Y-%m-%d %H:%M')
  228. start_datetime = start_datetime.replace(second=0)
  229. start_datetime_timezone = timezone.localize(start_datetime)
  230. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  231. sql_query += ' and m.created_at >= :start'
  232. arg_dict['start'] = start_datetime_utc
  233. if args['end']:
  234. end_datetime = datetime.strptime(args['end'], '%Y-%m-%d %H:%M')
  235. end_datetime = end_datetime.replace(second=0)
  236. end_datetime_timezone = timezone.localize(end_datetime)
  237. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  238. sql_query += ' and m.created_at < :end'
  239. arg_dict['end'] = end_datetime_utc
  240. sql_query += ' GROUP BY date order by date'
  241. with db.engine.begin() as conn:
  242. rs = conn.execute(db.text(sql_query), arg_dict)
  243. response_data = []
  244. for i in rs:
  245. response_data.append({
  246. 'date': str(i.date),
  247. 'rate': round((i.feedback_count * 1000 / i.message_count) if i.message_count > 0 else 0, 2),
  248. })
  249. return jsonify({
  250. 'data': response_data
  251. })
  252. class AverageResponseTimeStatistic(Resource):
  253. @setup_required
  254. @login_required
  255. @account_initialization_required
  256. def get(self, app_id):
  257. account = current_user
  258. app_id = str(app_id)
  259. app_model = _get_app(app_id, 'completion')
  260. parser = reqparse.RequestParser()
  261. parser.add_argument('start', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  262. parser.add_argument('end', type=datetime_string('%Y-%m-%d %H:%M'), location='args')
  263. args = parser.parse_args()
  264. sql_query = '''
  265. SELECT date(DATE_TRUNC('day', created_at AT TIME ZONE 'UTC' AT TIME ZONE :tz )) AS date,
  266. AVG(provider_response_latency) as latency
  267. FROM messages
  268. WHERE app_id = :app_id
  269. '''
  270. arg_dict = {'tz': account.timezone, 'app_id': app_model.id}
  271. timezone = pytz.timezone(account.timezone)
  272. utc_timezone = pytz.utc
  273. if args['start']:
  274. start_datetime = datetime.strptime(args['start'], '%Y-%m-%d %H:%M')
  275. start_datetime = start_datetime.replace(second=0)
  276. start_datetime_timezone = timezone.localize(start_datetime)
  277. start_datetime_utc = start_datetime_timezone.astimezone(utc_timezone)
  278. sql_query += ' and created_at >= :start'
  279. arg_dict['start'] = start_datetime_utc
  280. if args['end']:
  281. end_datetime = datetime.strptime(args['end'], '%Y-%m-%d %H:%M')
  282. end_datetime = end_datetime.replace(second=0)
  283. end_datetime_timezone = timezone.localize(end_datetime)
  284. end_datetime_utc = end_datetime_timezone.astimezone(utc_timezone)
  285. sql_query += ' and created_at < :end'
  286. arg_dict['end'] = end_datetime_utc
  287. sql_query += ' GROUP BY date order by date'
  288. with db.engine.begin() as conn:
  289. rs = conn.execute(db.text(sql_query), arg_dict)
  290. response_data = []
  291. for i in rs:
  292. response_data.append({
  293. 'date': str(i.date),
  294. 'latency': round(i.latency * 1000, 4)
  295. })
  296. return jsonify({
  297. 'data': response_data
  298. })
  299. api.add_resource(DailyConversationStatistic, '/apps/<uuid:app_id>/statistics/daily-conversations')
  300. api.add_resource(DailyTerminalsStatistic, '/apps/<uuid:app_id>/statistics/daily-end-users')
  301. api.add_resource(DailyTokenCostStatistic, '/apps/<uuid:app_id>/statistics/token-costs')
  302. api.add_resource(AverageSessionInteractionStatistic, '/apps/<uuid:app_id>/statistics/average-session-interactions')
  303. api.add_resource(UserSatisfactionRateStatistic, '/apps/<uuid:app_id>/statistics/user-satisfaction-rate')
  304. api.add_resource(AverageResponseTimeStatistic, '/apps/<uuid:app_id>/statistics/average-response-time')