application_stats.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. # coding=utf-8
  2. """
  3. @project: MaxKB
  4. @Author:虎虎
  5. @file: application_stats.py
  6. @date:2025/6/9 20:34
  7. @desc:
  8. """
  9. import datetime
  10. import os
  11. from typing import Dict, List
  12. from django.db import models
  13. from django.db.models import QuerySet
  14. from django.utils.translation import gettext_lazy as _
  15. from django.utils import timezone
  16. from rest_framework import serializers
  17. from application.models import ApplicationChatUserStats, Application
  18. from common.db.search import native_search, get_dynamics_model
  19. from common.exception.app_exception import AppApiException
  20. from common.utils.common import get_file_content
  21. from maxkb.conf import PROJECT_DIR
  22. from maxkb.settings import edition
  23. class ApplicationStatsSerializer(serializers.Serializer):
  24. chat_record_count = serializers.IntegerField(required=True, label=_("Number of conversations"))
  25. customer_added_count = serializers.IntegerField(required=True, label=_("Number of new users"))
  26. customer_num = serializers.IntegerField(required=True, label=_("Total number of users"))
  27. day = serializers.CharField(required=True, label=_("date"))
  28. star_num = serializers.IntegerField(required=True, label=_("Number of Likes"))
  29. tokens_num = serializers.IntegerField(required=True, label=_("Tokens consumption"))
  30. trample_num = serializers.IntegerField(required=True, label=_("Number of thumbs-downs"))
  31. class ApplicationStatisticsSerializer(serializers.Serializer):
  32. workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
  33. application_id = serializers.UUIDField(required=True, label=_("Application ID"))
  34. start_time = serializers.DateField(format='%Y-%m-%d', label=_("Start time"))
  35. end_time = serializers.DateField(format='%Y-%m-%d', label=_("End time"))
  36. def is_valid(self, *, raise_exception=False):
  37. super().is_valid(raise_exception=True)
  38. workspace_id = self.data.get('workspace_id')
  39. query_set = QuerySet(Application).filter(id=self.data.get('application_id'))
  40. if workspace_id:
  41. query_set = query_set.filter(workspace_id=workspace_id)
  42. if not query_set.exists():
  43. raise AppApiException(500, _('Application id does not exist'))
  44. def get_end_time(self):
  45. d = datetime.datetime.strptime(self.data.get('end_time'), '%Y-%m-%d').date()
  46. naive = datetime.datetime.combine(d, datetime.time.max)
  47. return timezone.make_aware(naive, timezone.get_default_timezone())
  48. def get_start_time(self):
  49. d = datetime.datetime.strptime(self.data.get('start_time'), '%Y-%m-%d').date()
  50. naive = datetime.datetime.combine(d, datetime.time.min)
  51. return timezone.make_aware(naive, timezone.get_default_timezone())
  52. def get_customer_count_trend(self, with_valid=True):
  53. if with_valid:
  54. self.is_valid(raise_exception=True)
  55. start_time = self.get_start_time()
  56. end_time = self.get_end_time()
  57. return native_search(
  58. {'default_sql': QuerySet(ApplicationChatUserStats).filter(
  59. application_id=self.data.get('application_id'),
  60. create_time__gte=start_time,
  61. create_time__lte=end_time)},
  62. select_string=get_file_content(
  63. os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'customer_count_trend.sql')))
  64. def get_chat_record_aggregate_trend(self, with_valid=True):
  65. if with_valid:
  66. self.is_valid(raise_exception=True)
  67. start_time = self.get_start_time()
  68. end_time = self.get_end_time()
  69. chat_record_aggregate_trend = native_search(
  70. {'default_sql': QuerySet(model=get_dynamics_model(
  71. {'application_chat.application_id': models.UUIDField(),
  72. 'application_chat_record.create_time': models.DateTimeField()})).filter(
  73. **{'application_chat.application_id': self.data.get('application_id'),
  74. 'application_chat_record.create_time__gte': start_time,
  75. 'application_chat_record.create_time__lte': end_time}
  76. )},
  77. select_string=get_file_content(
  78. os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'chat_record_count_trend.sql')))
  79. customer_count_trend = self.get_customer_count_trend(with_valid=False)
  80. return self.merge_customer_chat_record(chat_record_aggregate_trend, customer_count_trend)
  81. def merge_customer_chat_record(self, chat_record_aggregate_trend: List[Dict], customer_count_trend: List[Dict]):
  82. return [{**self.find(chat_record_aggregate_trend, lambda c: c.get('day').strftime('%Y-%m-%d') == day,
  83. {'star_num': 0, 'trample_num': 0, 'tokens_num': 0, 'chat_record_count': 0,
  84. 'customer_num': 0,
  85. 'day': day}),
  86. **self.find(customer_count_trend, lambda c: c.get('day').strftime('%Y-%m-%d') == day,
  87. {'customer_added_count': 0})}
  88. for
  89. day in
  90. self.get_days_between_dates(self.data.get('start_time'), self.data.get('end_time'))]
  91. @staticmethod
  92. def find(source_list, condition, default):
  93. value_list = [row for row in source_list if condition(row)]
  94. if len(value_list) > 0:
  95. return value_list[0]
  96. return default
  97. @staticmethod
  98. def get_days_between_dates(start_date, end_date):
  99. start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
  100. end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d')
  101. days = []
  102. current_date = start_date
  103. while current_date <= end_date:
  104. days.append(current_date.strftime('%Y-%m-%d'))
  105. current_date += datetime.timedelta(days=1)
  106. return days
  107. def get_token_usage_statistics(self, with_valid=True):
  108. if with_valid:
  109. self.is_valid(raise_exception=True)
  110. start_time = self.get_start_time()
  111. end_time = self.get_end_time()
  112. get_token_usage = native_search(
  113. {'default_sql': QuerySet(model=get_dynamics_model(
  114. {'application_chat.application_id': models.UUIDField(),
  115. 'application_chat_record.create_time': models.DateTimeField()})).filter(
  116. **{'application_chat.application_id': self.data.get('application_id'),
  117. 'application_chat_record.create_time__gte': start_time,
  118. 'application_chat_record.create_time__lte': end_time}
  119. )},
  120. select_string=get_file_content(
  121. os.path.join(PROJECT_DIR, "apps", "application", 'sql',
  122. ('get_token_usage_ee.sql' if ['PE', 'EE'].__contains__(
  123. edition) else 'get_token_usage.sql'))))
  124. return get_token_usage
  125. def get_top_questions_statistics(self, with_valid=True):
  126. if with_valid:
  127. self.is_valid(raise_exception=True)
  128. start_time = self.get_start_time()
  129. end_time = self.get_end_time()
  130. get_top_questions = native_search(
  131. {'default_sql': QuerySet(model=get_dynamics_model(
  132. {'application_chat.application_id': models.UUIDField(),
  133. 'application_chat_record.create_time': models.DateTimeField()})).filter(
  134. **{'application_chat.application_id': self.data.get('application_id'),
  135. 'application_chat_record.create_time__gte': start_time,
  136. 'application_chat_record.create_time__lte': end_time}
  137. )},
  138. select_string=get_file_content(
  139. os.path.join(PROJECT_DIR, "apps", "application", 'sql', (
  140. 'top_questions_ee.sql' if ['PE', 'EE'].__contains__(edition) else 'top_questions.sql'))))
  141. return get_top_questions