perf: add charts_map

pull/15630/head
feng 2025-07-29 16:16:55 +08:00
parent 66f74f9c30
commit 060ef38169
3 changed files with 49 additions and 11 deletions

View File

@ -2,7 +2,7 @@
# #
from collections import defaultdict from collections import defaultdict
from django.db.models import Count from django.db.models import Count, Q
from django.http.response import JsonResponse from django.http.response import JsonResponse
from rest_framework.views import APIView from rest_framework.views import APIView
@ -15,6 +15,9 @@ from reports.mixins import DateRangeMixin
__all__ = ['UserReportApi'] __all__ = ['UserReportApi']
from users.models import User
from users.models.user import Source
class UserReportApi(DateRangeMixin, APIView): class UserReportApi(DateRangeMixin, APIView):
http_method_names = ['get'] http_method_names = ['get']
@ -87,16 +90,36 @@ class UserReportApi(DateRangeMixin, APIView):
queryset = UserLoginLog.objects.filter(status=LoginStatusChoices.failed) queryset = UserLoginLog.objects.filter(status=LoginStatusChoices.failed)
return UserLoginLog.filter_queryset_by_org(queryset) return UserLoginLog.filter_queryset_by_org(queryset)
@lazyproperty
def user_qs(self):
return User.get_org_users()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
data = {} data = {}
user_stats = self.user_qs.aggregate(
total=Count(1),
first_login=Count(1, filter=Q(is_first_login=True)),
need_update_password=Count(1, filter=Q(need_update_password=True)),
face_vector=Count(1, filter=Q(face_vector__isnull=False)),
not_enabled_mfa=Count(1, filter=Q(mfa_level=0)),
)
user_stats['valid'] = sum(1 for u in self.user_qs if u.is_valid)
data['user_stats'] = user_stats
source_map = Source.as_dict()
user_by_source = defaultdict(int)
for source in self.user_qs.values_list('source', flat=True):
k = source_map.get(source, source)
user_by_source[str(k)] += 1
data['user_by_source'] = [{'name': k, 'value': v} for k, v in user_by_source.items()]
data['user_login_log_metrics'] = { data['user_login_log_metrics'] = {
'dates_metrics_date': self.dates_metrics_date, 'dates_metrics_date': self.dates_metrics_date,
'dates_metrics_total': self.get_user_login_metrics(self.user_login_log_queryset), 'dates_metrics_success_total': self.get_user_login_metrics(self.user_login_log_queryset),
} 'dates_metrics_failure_total': self.get_user_login_metrics(self.user_login_failed_queryset),
data['user_login_failed_metrics'] = {
'dates_metrics_date': self.dates_metrics_date,
'dates_metrics_total': self.get_user_login_metrics(self.user_login_failed_queryset),
} }
data['user_login_method_metrics'] = { data['user_login_method_metrics'] = {
'dates_metrics_date': self.dates_metrics_date, 'dates_metrics_date': self.dates_metrics_date,

View File

@ -6,8 +6,7 @@ from urllib.parse import urlparse
from django.conf import settings from django.conf import settings
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.http import FileResponse, HttpResponseBadRequest from django.http import FileResponse, HttpResponseBadRequest, JsonResponse
from django.http import JsonResponse
from django.utils import timezone from django.utils import timezone
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views import View from django.views import View
@ -16,9 +15,21 @@ from pdf2image import convert_from_bytes
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
charts_map = { charts_map = {
"UserActivity": { "UserLoginActivity": {
"title": "用户活动", "title": "用户登录活动",
"path": "/ui/#/reports/users/user-activity" "path": "/ui/#/reports/users/user-activity"
},
"UserPasswordChange": {
"title": "用户改密记录",
"path": "/ui/#/reports/users/change-password"
},
"AssetStatistics": {
"title": "资产统计概览",
"path": "/ui/#/reports/assets/asset-statistics"
},
"AssetAccessActivity": {
"title": "资产访问活动",
"path": "/ui/#/reports/assets/asset-activity"
} }
} }
@ -81,7 +92,7 @@ class ExportPdfView(View):
sessionid = request.COOKIES.get(settings.SESSION_COOKIE_NAME) sessionid = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
if not sessionid: if not sessionid:
return HttpResponseBadRequest('No sessionid found in cookies') return HttpResponseBadRequest('No sessionid found in cookies')
pdf_bytes, title = export_chart_to_pdf(chart_name, sessionid, request=request) pdf_bytes, title = export_chart_to_pdf(chart_name, sessionid, request=request)
if not pdf_bytes: if not pdf_bytes:
return HttpResponseBadRequest('Failed to generate PDF') return HttpResponseBadRequest('Failed to generate PDF')

View File

@ -23,6 +23,10 @@ class Source(models.TextChoices):
slack = "slack", _("Slack") slack = "slack", _("Slack")
custom = "custom", "Custom" custom = "custom", "Custom"
@classmethod
def as_dict(cls):
return {choice.value: choice.label for choice in cls}
class SourceMixin: class SourceMixin:
source: str source: str