django-cfg 1.4.82__py3-none-any.whl → 1.4.84__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/centrifugo/serializers/channels.py +3 -0
- django_cfg/apps/centrifugo/serializers/publishes.py +2 -0
- django_cfg/apps/centrifugo/views/admin_api.py +1 -1
- django_cfg/apps/centrifugo/views/monitoring.py +116 -6
- django_cfg/apps/centrifugo/views/testing_api.py +1 -1
- django_cfg/apps/dashboard/__init__.py +8 -0
- django_cfg/apps/dashboard/api/__init__.py +27 -0
- django_cfg/apps/dashboard/api/serializers.py +165 -0
- django_cfg/apps/dashboard/api/viewsets.py +257 -0
- django_cfg/apps/dashboard/apps.py +23 -0
- django_cfg/apps/dashboard/services/__init__.py +11 -0
- django_cfg/apps/dashboard/services/statistics_service.py +235 -0
- django_cfg/apps/dashboard/services/system_health_service.py +280 -0
- django_cfg/apps/dashboard/urls.py +23 -0
- django_cfg/apps/frontend/JWT_AUTO_INJECTION.md +224 -0
- django_cfg/apps/frontend/views.py +121 -7
- django_cfg/apps/tasks/api/serializers.py +82 -0
- django_cfg/apps/tasks/api/views.py +571 -0
- django_cfg/apps/urls.py +2 -1
- django_cfg/core/builders/apps_builder.py +1 -0
- django_cfg/middleware/README.md +12 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +1 -1
- django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +7 -10
- django_cfg/modules/django_client/core/parser/openapi30.py +26 -1
- django_cfg/modules/django_client/core/parser/openapi31.py +26 -1
- django_cfg/pyproject.toml +1 -1
- django_cfg/static/frontend/admin/404.html +1 -1
- django_cfg/static/frontend/admin/500.html +1 -1
- django_cfg/static/frontend/admin/_next/static/-Zk0eDB7OJOEFrFyR5BwZ/_buildManifest.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{43076.55dd23b6cd68edb0.js → 20695.a7d37b6c40ad3f58.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{25033.d626f78bc99bc4a1.js → 25033.ee3e206d5a2877b6.js} +2 -2
- django_cfg/static/frontend/admin/_next/static/chunks/{25892.964150a58f94ce06.js → 25892.5cbed319f9226fdc.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{2d7a934f.dfef67639279d59d.js → 2d7a934f.329c61f23af1a7ec.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{30649.00c679812a56aee3.js → 30649.963cfb7268b5864a.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{30875.784491146c38dbcb.js → 30875.82c3741757b8aa32.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{32163.ab0ca435b3f26c04.js → 32163.109a03a7252f1508.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/43076-4be6a9794e9c3e8b.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{49978.fb8ba7ee52ffe666.js → 49978.db5a86a8eb233f35.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-79c02212788f1ec7.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{50319.f786248384877960.js → 50319.fd78c7f7e3f1966e.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{52908.b690e323d8f8efdd.js → 52908.da5b850b0bc0970c.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{53710.80ca863525d137db.js → 53710.7176bbee6c7b78be.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{57982.251fed8d58adcf53.js → 57982.2c90b33b0934522a.js} +2 -2
- django_cfg/static/frontend/admin/_next/static/chunks/{60181.86e18057c4caaa97.js → 60181.c94d78d10eb5da37.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{60374.bde0ec1249aa79c6.js → 60374.5d80cfc45439b2b0.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{6884.7b1db804c88280ed.js → 6884.624d563508cf6db4.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{69436.9515b854cdf4b57a.js → 69436.be44021e3d7c99c7.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{70628.00cdd98f672e684f.js → 70628.58e8c38a66543d5e.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{73218.a826c2248612b37f.js → 73218.d712e7bd678e23a8.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{76334.64fbaa923d9ac293.js → 76334.f43f2d8b4bbf8dd6.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{7799.2b280f8ddf067d49.js → 7799.1575cc212bc750c7.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{80574.620a8a5b4eb91c25.js → 80574.92638dd7b9979664.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{81127.a0603c3394892d4e.js → 81127.3ead500eec887152.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/82296-a2c8d38f62224be5.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{8383.eb6188b22c453e14.js → 8383.e25a442df26b2e26.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{85833.35e6ca25ac32a7d2.js → 85833.b0dead4fbcbfdd1b.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{95365.fc9d7653a78839d0.js → 95365.2b430045fc2e5acf.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{96424.0793b94836eb13a6.js → 96424.11d76570e9a94b85.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/404-c283223d1afd02a2.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/500-389d6d3e1f2f7fda.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-f25bec36bbdc9625.js +272 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/_error-5291033275c26d09.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/index-d7bc30185f52cbca.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-24588bf5551f30df.js → cookies-b39c7f22c066e2c6.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-354dae34a4c4da59.js → privacy-5aedad0cf3a4f80f.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-0a5d7fa591ebb1ae.js → security-dbd854d0d5d483e2.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-c3d80322f52dc112.js → terms-f3e1d2b9e5edf12f.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-22532c65971225eb.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/profile-e93a65e8e7d9022b.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/ui-669e8f2a785beba2.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-a8a9ba76f2c75354.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{webpack-905bba30877f6490.js → webpack-92add5f95c66e349.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/css/78d677ac1677c210.css +3 -0
- django_cfg/static/frontend/admin/auth.html +1 -1
- django_cfg/static/frontend/admin/index.html +1 -1
- django_cfg/static/frontend/admin/legal/cookies.html +1 -1
- django_cfg/static/frontend/admin/legal/privacy.html +1 -1
- django_cfg/static/frontend/admin/legal/security.html +1 -1
- django_cfg/static/frontend/admin/legal/terms.html +1 -1
- django_cfg/static/frontend/admin/private/centrifugo.html +1 -0
- django_cfg/static/frontend/admin/private/profile.html +1 -0
- django_cfg/static/frontend/admin/private/ui.html +1 -0
- django_cfg/static/frontend/admin/private.html +1 -1
- django_cfg/templates/admin/index.html +97 -63
- django_cfg/templates/admin_old/index.html +80 -0
- django_cfg/templatetags/django_cfg.py +57 -10
- {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/METADATA +1 -1
- {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/RECORD +142 -122
- django_cfg/static/frontend/admin/_next/static/chunks/pages/404-7cdad2942c3fb179.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/500-6cdb27b00678364f.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-9c5ca2471de6b000.js +0 -272
- django_cfg/static/frontend/admin/_next/static/chunks/pages/_error-b8071a05cabe1c2d.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/index-bf88192a30e013a9.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-2f58633ddf63a5bc.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/ui-73632f2d9c6b11ab.js +0 -1
- django_cfg/static/frontend/admin/_next/static/css/e201974f9a4d64e6.css +0 -3
- django_cfg/static/frontend/admin/_next/static/qEBrQJUidlI_maQ4xQnI0/_buildManifest.js +0 -1
- django_cfg/static/frontend/admin/ui.html +0 -92
- /django_cfg/static/frontend/admin/_next/static/{qEBrQJUidlI_maQ4xQnI0 → -Zk0eDB7OJOEFrFyR5BwZ}/_ssgManifest.js +0 -0
- /django_cfg/templates/{admin → admin_old}/components/action_grid.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/card.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/data_table.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/metric_card.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/modal.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/progress_bar.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/section_header.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/stat_item.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/stats_grid.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/status_badge.html +0 -0
- /django_cfg/templates/{admin → admin_old}/components/user_avatar.html +0 -0
- /django_cfg/templates/{admin → admin_old}/constance/change_list.html +0 -0
- /django_cfg/templates/{admin → admin_old}/constance/includes/default_value.html +0 -0
- /django_cfg/templates/{admin → admin_old}/constance/includes/fieldset_header.html +0 -0
- /django_cfg/templates/{admin → admin_old}/constance/includes/results_list.html +0 -0
- /django_cfg/templates/{admin → admin_old}/constance/includes/setting_row.html +0 -0
- /django_cfg/templates/{admin → admin_old}/constance/includes/table_headers.html +0 -0
- /django_cfg/templates/{admin → admin_old}/examples/component_class_example.html +0 -0
- /django_cfg/templates/{admin → admin_old}/import_export/change_list_export.html +0 -0
- /django_cfg/templates/{admin → admin_old}/import_export/change_list_import.html +0 -0
- /django_cfg/templates/{admin → admin_old}/import_export/change_list_import_export.html +0 -0
- /django_cfg/templates/{admin → admin_old}/index_new.html +0 -0
- /django_cfg/templates/{admin → admin_old}/layouts/base_dashboard.html +0 -0
- /django_cfg/templates/{admin → admin_old}/layouts/dashboard_with_tabs.html +0 -0
- /django_cfg/templates/{admin → admin_old}/sections/commands_section.html +0 -0
- /django_cfg/templates/{admin → admin_old}/sections/documentation_section.html +0 -0
- /django_cfg/templates/{admin → admin_old}/sections/overview_section.html +0 -0
- /django_cfg/templates/{admin → admin_old}/sections/stats_section.html +0 -0
- /django_cfg/templates/{admin → admin_old}/sections/system_section.html +0 -0
- /django_cfg/templates/{admin → admin_old}/sections/widgets_section.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/activity_tracker.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/charts_section.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/django_commands.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/quick_actions.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/recent_activity_improved.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/recent_users_table.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/stats_cards.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/stats_tiles.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/system_health.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/system_metrics.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/components/user_permissions.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/tabs/app_stats_tab.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/tabs/commands_tab.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/tabs/documentation_tab.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/tabs/overview_tab.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/tabs/stats_tab.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/tabs/users_tab.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/tabs/widgets_tab.html +0 -0
- /django_cfg/templates/{admin → admin_old}/snippets/zones/zones_table.html +0 -0
- {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dashboard API ViewSets
|
|
3
|
+
|
|
4
|
+
Provides RESTful endpoints for dashboard data.
|
|
5
|
+
No ORM dependencies - uses services for data collection.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema, inline_serializer
|
|
13
|
+
from drf_spectacular.types import OpenApiTypes
|
|
14
|
+
from rest_framework import serializers, status, viewsets
|
|
15
|
+
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
|
16
|
+
from rest_framework.decorators import action
|
|
17
|
+
from rest_framework.permissions import IsAdminUser
|
|
18
|
+
from rest_framework.response import Response
|
|
19
|
+
|
|
20
|
+
from ..services import StatisticsService, SystemHealthService
|
|
21
|
+
from .serializers import (
|
|
22
|
+
ActivityEntrySerializer,
|
|
23
|
+
APIResponseSerializer,
|
|
24
|
+
AppStatisticsSerializer,
|
|
25
|
+
DashboardOverviewSerializer,
|
|
26
|
+
QuickActionSerializer,
|
|
27
|
+
StatCardSerializer,
|
|
28
|
+
SystemHealthItemSerializer,
|
|
29
|
+
SystemHealthSerializer,
|
|
30
|
+
SystemMetricsSerializer,
|
|
31
|
+
UserStatisticsSerializer,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DashboardViewSet(viewsets.GenericViewSet):
|
|
38
|
+
"""
|
|
39
|
+
Dashboard Data ViewSet
|
|
40
|
+
|
|
41
|
+
Provides comprehensive dashboard endpoints for Next.js frontend.
|
|
42
|
+
All data is collected via services without ORM dependencies.
|
|
43
|
+
|
|
44
|
+
%%PRIORITY:HIGH%%
|
|
45
|
+
%%AI_HINT: Main entry point for dashboard API%%
|
|
46
|
+
|
|
47
|
+
TAGS: dashboard, api, viewset
|
|
48
|
+
USED_BY: Next.js admin panel
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
authentication_classes = [SessionAuthentication, BasicAuthentication]
|
|
52
|
+
permission_classes = [IsAdminUser]
|
|
53
|
+
serializer_class = DashboardOverviewSerializer
|
|
54
|
+
|
|
55
|
+
@action(detail=False, methods=['get'], url_path='overview')
|
|
56
|
+
@extend_schema(
|
|
57
|
+
summary="Get dashboard overview",
|
|
58
|
+
description="Retrieve complete dashboard data including stats, health, actions, and metrics",
|
|
59
|
+
responses={200: DashboardOverviewSerializer},
|
|
60
|
+
tags=["Dashboard"]
|
|
61
|
+
)
|
|
62
|
+
def overview(self, request):
|
|
63
|
+
"""
|
|
64
|
+
Get complete dashboard overview.
|
|
65
|
+
|
|
66
|
+
Returns all dashboard data in a single request:
|
|
67
|
+
- Statistics cards
|
|
68
|
+
- System health status
|
|
69
|
+
- Quick actions
|
|
70
|
+
- Recent activity
|
|
71
|
+
- System metrics
|
|
72
|
+
- User statistics
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
stats_service = StatisticsService()
|
|
76
|
+
health_service = SystemHealthService()
|
|
77
|
+
|
|
78
|
+
data = {
|
|
79
|
+
'stat_cards': stats_service.get_stat_cards(),
|
|
80
|
+
'system_health': health_service.get_all_health_checks(),
|
|
81
|
+
'quick_actions': health_service.get_quick_actions(),
|
|
82
|
+
'recent_activity': stats_service.get_recent_activity(limit=10),
|
|
83
|
+
'system_metrics': stats_service.get_system_metrics(),
|
|
84
|
+
'user_statistics': stats_service.get_user_statistics(),
|
|
85
|
+
'timestamp': datetime.now().isoformat(),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return Response(data)
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"Dashboard overview API error: {e}", exc_info=True)
|
|
92
|
+
return Response({
|
|
93
|
+
'error': str(e)
|
|
94
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
95
|
+
|
|
96
|
+
@extend_schema(
|
|
97
|
+
summary="Get statistics cards",
|
|
98
|
+
description="Retrieve dashboard statistics cards with key metrics",
|
|
99
|
+
responses=StatCardSerializer(many=True),
|
|
100
|
+
tags=["dashboard"]
|
|
101
|
+
)
|
|
102
|
+
@action(detail=False, methods=['get'], url_path='stats/cards', pagination_class=None, serializer_class=StatCardSerializer)
|
|
103
|
+
def stat_cards(self, request):
|
|
104
|
+
"""Get dashboard statistics cards."""
|
|
105
|
+
try:
|
|
106
|
+
stats_service = StatisticsService()
|
|
107
|
+
cards = stats_service.get_stat_cards()
|
|
108
|
+
|
|
109
|
+
return Response(cards)
|
|
110
|
+
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"Stat cards API error: {e}")
|
|
113
|
+
return Response({
|
|
114
|
+
'error': str(e)
|
|
115
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
116
|
+
|
|
117
|
+
@extend_schema(
|
|
118
|
+
summary="Get system health status",
|
|
119
|
+
description="Retrieve overall system health including all component checks",
|
|
120
|
+
responses={200: SystemHealthSerializer},
|
|
121
|
+
tags=["dashboard"]
|
|
122
|
+
)
|
|
123
|
+
@action(detail=False, methods=['get'], url_path='health', serializer_class=SystemHealthSerializer)
|
|
124
|
+
def system_health(self, request):
|
|
125
|
+
"""Get overall system health status."""
|
|
126
|
+
try:
|
|
127
|
+
health_service = SystemHealthService()
|
|
128
|
+
health_data = health_service.get_overall_health_status()
|
|
129
|
+
|
|
130
|
+
return Response(health_data)
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(f"System health API error: {e}")
|
|
134
|
+
return Response({
|
|
135
|
+
'error': str(e)
|
|
136
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
137
|
+
|
|
138
|
+
@extend_schema(
|
|
139
|
+
summary="Get quick actions",
|
|
140
|
+
description="Retrieve quick action buttons for dashboard",
|
|
141
|
+
responses=QuickActionSerializer(many=True),
|
|
142
|
+
tags=["Dashboard"]
|
|
143
|
+
)
|
|
144
|
+
@action(detail=False, methods=['get'], url_path='actions', pagination_class=None, serializer_class=QuickActionSerializer)
|
|
145
|
+
def quick_actions(self, request):
|
|
146
|
+
"""Get quick action buttons."""
|
|
147
|
+
try:
|
|
148
|
+
health_service = SystemHealthService()
|
|
149
|
+
actions = health_service.get_quick_actions()
|
|
150
|
+
|
|
151
|
+
return Response(actions)
|
|
152
|
+
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.error(f"Quick actions API error: {e}")
|
|
155
|
+
return Response({
|
|
156
|
+
'error': str(e)
|
|
157
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
158
|
+
|
|
159
|
+
@extend_schema(
|
|
160
|
+
summary="Get recent activity",
|
|
161
|
+
description="Retrieve recent system activity entries",
|
|
162
|
+
parameters=[
|
|
163
|
+
OpenApiParameter(
|
|
164
|
+
name='limit',
|
|
165
|
+
description='Maximum number of entries to return',
|
|
166
|
+
required=False,
|
|
167
|
+
type=int,
|
|
168
|
+
default=10
|
|
169
|
+
),
|
|
170
|
+
],
|
|
171
|
+
responses=ActivityEntrySerializer(many=True),
|
|
172
|
+
tags=["dashboard"]
|
|
173
|
+
)
|
|
174
|
+
@action(detail=False, methods=['get'], url_path='activity', pagination_class=None, serializer_class=ActivityEntrySerializer)
|
|
175
|
+
def recent_activity(self, request):
|
|
176
|
+
"""Get recent activity entries."""
|
|
177
|
+
try:
|
|
178
|
+
limit = int(request.query_params.get('limit', 10))
|
|
179
|
+
stats_service = StatisticsService()
|
|
180
|
+
activity = stats_service.get_recent_activity(limit=limit)
|
|
181
|
+
|
|
182
|
+
return Response(activity)
|
|
183
|
+
|
|
184
|
+
except Exception as e:
|
|
185
|
+
logger.error(f"Recent activity API error: {e}")
|
|
186
|
+
return Response({
|
|
187
|
+
'error': str(e)
|
|
188
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
189
|
+
|
|
190
|
+
@extend_schema(
|
|
191
|
+
summary="Get system metrics",
|
|
192
|
+
description="Retrieve system performance metrics (CPU, memory, disk, etc.)",
|
|
193
|
+
responses={200: SystemMetricsSerializer},
|
|
194
|
+
tags=["dashboard"]
|
|
195
|
+
)
|
|
196
|
+
@action(detail=False, methods=['get'], url_path='metrics', serializer_class=SystemMetricsSerializer)
|
|
197
|
+
def system_metrics(self, request):
|
|
198
|
+
"""Get system performance metrics."""
|
|
199
|
+
try:
|
|
200
|
+
stats_service = StatisticsService()
|
|
201
|
+
metrics = stats_service.get_system_metrics()
|
|
202
|
+
|
|
203
|
+
return Response(metrics)
|
|
204
|
+
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.error(f"System metrics API error: {e}")
|
|
207
|
+
return Response({
|
|
208
|
+
'error': str(e)
|
|
209
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
210
|
+
|
|
211
|
+
@extend_schema(
|
|
212
|
+
summary="Get user statistics",
|
|
213
|
+
description="Retrieve user-related statistics",
|
|
214
|
+
responses={200: UserStatisticsSerializer},
|
|
215
|
+
tags=["dashboard"]
|
|
216
|
+
)
|
|
217
|
+
@action(detail=False, methods=['get'], url_path='stats/users', serializer_class=UserStatisticsSerializer)
|
|
218
|
+
def user_statistics(self, request):
|
|
219
|
+
"""Get user statistics."""
|
|
220
|
+
try:
|
|
221
|
+
stats_service = StatisticsService()
|
|
222
|
+
user_stats = stats_service.get_user_statistics()
|
|
223
|
+
|
|
224
|
+
return Response(user_stats)
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
logger.error(f"User statistics API error: {e}")
|
|
228
|
+
return Response({
|
|
229
|
+
'error': str(e)
|
|
230
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
231
|
+
|
|
232
|
+
@extend_schema(
|
|
233
|
+
summary="Get application statistics",
|
|
234
|
+
description="Retrieve statistics for all enabled django-cfg applications",
|
|
235
|
+
responses=AppStatisticsSerializer(many=True),
|
|
236
|
+
tags=["dashboard"]
|
|
237
|
+
)
|
|
238
|
+
@action(detail=False, methods=['get'], url_path='stats/apps', pagination_class=None, serializer_class=AppStatisticsSerializer)
|
|
239
|
+
def app_statistics(self, request):
|
|
240
|
+
"""Get application-specific statistics."""
|
|
241
|
+
try:
|
|
242
|
+
stats_service = StatisticsService()
|
|
243
|
+
app_stats = stats_service.get_app_statistics()
|
|
244
|
+
|
|
245
|
+
# Convert dict to list of {app_name, statistics} objects
|
|
246
|
+
data = [
|
|
247
|
+
{'app_name': app_name, 'statistics': stats}
|
|
248
|
+
for app_name, stats in app_stats.items()
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
return Response(data)
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.error(f"App statistics API error: {e}")
|
|
255
|
+
return Response({
|
|
256
|
+
'error': str(e)
|
|
257
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dashboard App Configuration
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from django.apps import AppConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DashboardConfig(AppConfig):
|
|
9
|
+
"""Configuration for Dashboard application."""
|
|
10
|
+
|
|
11
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
12
|
+
name = 'django_cfg.apps.dashboard'
|
|
13
|
+
label = 'dashboard'
|
|
14
|
+
verbose_name = 'Dashboard'
|
|
15
|
+
|
|
16
|
+
def ready(self):
|
|
17
|
+
"""
|
|
18
|
+
Application initialization.
|
|
19
|
+
|
|
20
|
+
Called when Django starts. Import signals or perform
|
|
21
|
+
other initialization tasks here.
|
|
22
|
+
"""
|
|
23
|
+
pass
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dashboard Services
|
|
3
|
+
|
|
4
|
+
Business logic for collecting and aggregating dashboard data.
|
|
5
|
+
Services are separated from views/API layer for better testability and reusability.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .statistics_service import StatisticsService
|
|
9
|
+
from .system_health_service import SystemHealthService
|
|
10
|
+
|
|
11
|
+
__all__ = ['StatisticsService', 'SystemHealthService']
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Statistics Service
|
|
3
|
+
|
|
4
|
+
Collects and aggregates statistics for dashboard display.
|
|
5
|
+
No ORM dependencies - can work with any data source.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime, timedelta
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StatisticsService:
|
|
16
|
+
"""
|
|
17
|
+
Service for collecting dashboard statistics.
|
|
18
|
+
|
|
19
|
+
%%PRIORITY:HIGH%%
|
|
20
|
+
%%AI_HINT: This service collects data from various sources without ORM%%
|
|
21
|
+
|
|
22
|
+
TAGS: statistics, dashboard, service
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
"""Initialize statistics service."""
|
|
27
|
+
self.logger = logger
|
|
28
|
+
|
|
29
|
+
def get_user_statistics(self) -> Dict[str, Any]:
|
|
30
|
+
"""
|
|
31
|
+
Get user-related statistics.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dictionary containing user stats:
|
|
35
|
+
- total_users: Total number of users
|
|
36
|
+
- active_users: Number of active users (last 30 days)
|
|
37
|
+
- new_users: New users in last 7 days
|
|
38
|
+
- superusers: Number of superusers
|
|
39
|
+
|
|
40
|
+
%%AI_HINT: Currently returns mock data. Replace with real data source%%
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
# TODO: Replace with real data collection
|
|
44
|
+
# Example: from django.contrib.auth import get_user_model
|
|
45
|
+
# User = get_user_model()
|
|
46
|
+
# total_users = User.objects.count()
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
'total_users': 1250,
|
|
50
|
+
'active_users': 892,
|
|
51
|
+
'new_users': 45,
|
|
52
|
+
'superusers': 3,
|
|
53
|
+
}
|
|
54
|
+
except Exception as e:
|
|
55
|
+
self.logger.error(f"Error getting user statistics: {e}")
|
|
56
|
+
return {
|
|
57
|
+
'total_users': 0,
|
|
58
|
+
'active_users': 0,
|
|
59
|
+
'new_users': 0,
|
|
60
|
+
'superusers': 0,
|
|
61
|
+
'error': str(e)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def get_app_statistics(self) -> Dict[str, Dict[str, int]]:
|
|
65
|
+
"""
|
|
66
|
+
Get application-specific statistics.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Dictionary with app names as keys and their stats as values.
|
|
70
|
+
Example:
|
|
71
|
+
{
|
|
72
|
+
'leads': {'total': 450, 'active': 120},
|
|
73
|
+
'tasks': {'total': 1200, 'completed': 890},
|
|
74
|
+
...
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
%%AI_HINT: Aggregates statistics from different django-cfg apps%%
|
|
78
|
+
"""
|
|
79
|
+
try:
|
|
80
|
+
# TODO: Collect real statistics from enabled apps
|
|
81
|
+
return {
|
|
82
|
+
'leads': {
|
|
83
|
+
'total': 450,
|
|
84
|
+
'active': 120,
|
|
85
|
+
'converted': 85,
|
|
86
|
+
},
|
|
87
|
+
'tasks': {
|
|
88
|
+
'total': 1200,
|
|
89
|
+
'completed': 890,
|
|
90
|
+
'pending': 310,
|
|
91
|
+
},
|
|
92
|
+
'support': {
|
|
93
|
+
'total': 340,
|
|
94
|
+
'open': 45,
|
|
95
|
+
'closed': 295,
|
|
96
|
+
},
|
|
97
|
+
'newsletter': {
|
|
98
|
+
'subscribers': 2500,
|
|
99
|
+
'campaigns': 12,
|
|
100
|
+
'sent': 28000,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
except Exception as e:
|
|
104
|
+
self.logger.error(f"Error getting app statistics: {e}")
|
|
105
|
+
return {'error': str(e)}
|
|
106
|
+
|
|
107
|
+
def get_stat_cards(self) -> List[Dict[str, Any]]:
|
|
108
|
+
"""
|
|
109
|
+
Get statistics cards for dashboard overview.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
List of stat card dictionaries ready for serialization.
|
|
113
|
+
Each card contains: title, value, icon, change, change_type
|
|
114
|
+
|
|
115
|
+
USED_BY: DashboardViewSet.overview endpoint
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
user_stats = self.get_user_statistics()
|
|
119
|
+
|
|
120
|
+
cards = [
|
|
121
|
+
{
|
|
122
|
+
'title': 'Total Users',
|
|
123
|
+
'value': str(user_stats['total_users']),
|
|
124
|
+
'icon': 'people',
|
|
125
|
+
'change': '+12%',
|
|
126
|
+
'change_type': 'positive',
|
|
127
|
+
'color': 'primary',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
'title': 'Active Users',
|
|
131
|
+
'value': str(user_stats['active_users']),
|
|
132
|
+
'icon': 'person',
|
|
133
|
+
'change': '+5%',
|
|
134
|
+
'change_type': 'positive',
|
|
135
|
+
'color': 'success',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
'title': 'New Users (7d)',
|
|
139
|
+
'value': str(user_stats['new_users']),
|
|
140
|
+
'icon': 'person_add',
|
|
141
|
+
'change': '-2%',
|
|
142
|
+
'change_type': 'negative',
|
|
143
|
+
'color': 'warning',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
'title': 'System Health',
|
|
147
|
+
'value': '98%',
|
|
148
|
+
'icon': 'health_and_safety',
|
|
149
|
+
'change': '+1%',
|
|
150
|
+
'change_type': 'positive',
|
|
151
|
+
'color': 'success',
|
|
152
|
+
},
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
return cards
|
|
156
|
+
|
|
157
|
+
except Exception as e:
|
|
158
|
+
self.logger.error(f"Error generating stat cards: {e}")
|
|
159
|
+
return []
|
|
160
|
+
|
|
161
|
+
def get_recent_activity(self, limit: int = 10) -> List[Dict[str, Any]]:
|
|
162
|
+
"""
|
|
163
|
+
Get recent activity entries for dashboard.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
limit: Maximum number of activity entries to return
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
List of recent activity entries
|
|
170
|
+
|
|
171
|
+
%%AI_HINT: Can be connected to django-auditlog or custom activity tracking%%
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
# TODO: Connect to real activity logging system
|
|
175
|
+
now = datetime.now()
|
|
176
|
+
|
|
177
|
+
activities = [
|
|
178
|
+
{
|
|
179
|
+
'id': 1,
|
|
180
|
+
'user': 'john@example.com',
|
|
181
|
+
'action': 'created',
|
|
182
|
+
'resource': 'Lead #1234',
|
|
183
|
+
'timestamp': (now - timedelta(minutes=5)).isoformat(),
|
|
184
|
+
'icon': 'add_circle',
|
|
185
|
+
'color': 'success',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
'id': 2,
|
|
189
|
+
'user': 'admin@example.com',
|
|
190
|
+
'action': 'updated',
|
|
191
|
+
'resource': 'User Settings',
|
|
192
|
+
'timestamp': (now - timedelta(minutes=15)).isoformat(),
|
|
193
|
+
'icon': 'edit',
|
|
194
|
+
'color': 'primary',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
'id': 3,
|
|
198
|
+
'user': 'sarah@example.com',
|
|
199
|
+
'action': 'completed',
|
|
200
|
+
'resource': 'Task #456',
|
|
201
|
+
'timestamp': (now - timedelta(hours=1)).isoformat(),
|
|
202
|
+
'icon': 'check_circle',
|
|
203
|
+
'color': 'success',
|
|
204
|
+
},
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
return activities[:limit]
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
self.logger.error(f"Error getting recent activity: {e}")
|
|
211
|
+
return []
|
|
212
|
+
|
|
213
|
+
def get_system_metrics(self) -> Dict[str, Any]:
|
|
214
|
+
"""
|
|
215
|
+
Get system performance metrics.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Dictionary with system metrics (CPU, memory, disk, etc.)
|
|
219
|
+
|
|
220
|
+
%%AI_HINT: Can use psutil or similar for real system metrics%%
|
|
221
|
+
"""
|
|
222
|
+
try:
|
|
223
|
+
# TODO: Collect real system metrics
|
|
224
|
+
return {
|
|
225
|
+
'cpu_usage': 45.2,
|
|
226
|
+
'memory_usage': 62.8,
|
|
227
|
+
'disk_usage': 38.5,
|
|
228
|
+
'network_in': '125 MB/s',
|
|
229
|
+
'network_out': '89 MB/s',
|
|
230
|
+
'response_time': '245 ms',
|
|
231
|
+
'uptime': '15 days, 3 hours',
|
|
232
|
+
}
|
|
233
|
+
except Exception as e:
|
|
234
|
+
self.logger.error(f"Error getting system metrics: {e}")
|
|
235
|
+
return {'error': str(e)}
|