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.

Files changed (152) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/centrifugo/serializers/channels.py +3 -0
  3. django_cfg/apps/centrifugo/serializers/publishes.py +2 -0
  4. django_cfg/apps/centrifugo/views/admin_api.py +1 -1
  5. django_cfg/apps/centrifugo/views/monitoring.py +116 -6
  6. django_cfg/apps/centrifugo/views/testing_api.py +1 -1
  7. django_cfg/apps/dashboard/__init__.py +8 -0
  8. django_cfg/apps/dashboard/api/__init__.py +27 -0
  9. django_cfg/apps/dashboard/api/serializers.py +165 -0
  10. django_cfg/apps/dashboard/api/viewsets.py +257 -0
  11. django_cfg/apps/dashboard/apps.py +23 -0
  12. django_cfg/apps/dashboard/services/__init__.py +11 -0
  13. django_cfg/apps/dashboard/services/statistics_service.py +235 -0
  14. django_cfg/apps/dashboard/services/system_health_service.py +280 -0
  15. django_cfg/apps/dashboard/urls.py +23 -0
  16. django_cfg/apps/frontend/JWT_AUTO_INJECTION.md +224 -0
  17. django_cfg/apps/frontend/views.py +121 -7
  18. django_cfg/apps/tasks/api/serializers.py +82 -0
  19. django_cfg/apps/tasks/api/views.py +571 -0
  20. django_cfg/apps/urls.py +2 -1
  21. django_cfg/core/builders/apps_builder.py +1 -0
  22. django_cfg/middleware/README.md +12 -0
  23. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +1 -1
  24. django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +7 -10
  25. django_cfg/modules/django_client/core/parser/openapi30.py +26 -1
  26. django_cfg/modules/django_client/core/parser/openapi31.py +26 -1
  27. django_cfg/pyproject.toml +1 -1
  28. django_cfg/static/frontend/admin/404.html +1 -1
  29. django_cfg/static/frontend/admin/500.html +1 -1
  30. django_cfg/static/frontend/admin/_next/static/-Zk0eDB7OJOEFrFyR5BwZ/_buildManifest.js +1 -0
  31. django_cfg/static/frontend/admin/_next/static/chunks/{43076.55dd23b6cd68edb0.js → 20695.a7d37b6c40ad3f58.js} +1 -1
  32. django_cfg/static/frontend/admin/_next/static/chunks/{25033.d626f78bc99bc4a1.js → 25033.ee3e206d5a2877b6.js} +2 -2
  33. django_cfg/static/frontend/admin/_next/static/chunks/{25892.964150a58f94ce06.js → 25892.5cbed319f9226fdc.js} +1 -1
  34. django_cfg/static/frontend/admin/_next/static/chunks/{2d7a934f.dfef67639279d59d.js → 2d7a934f.329c61f23af1a7ec.js} +1 -1
  35. django_cfg/static/frontend/admin/_next/static/chunks/{30649.00c679812a56aee3.js → 30649.963cfb7268b5864a.js} +1 -1
  36. django_cfg/static/frontend/admin/_next/static/chunks/{30875.784491146c38dbcb.js → 30875.82c3741757b8aa32.js} +1 -1
  37. django_cfg/static/frontend/admin/_next/static/chunks/{32163.ab0ca435b3f26c04.js → 32163.109a03a7252f1508.js} +1 -1
  38. django_cfg/static/frontend/admin/_next/static/chunks/43076-4be6a9794e9c3e8b.js +1 -0
  39. django_cfg/static/frontend/admin/_next/static/chunks/{49978.fb8ba7ee52ffe666.js → 49978.db5a86a8eb233f35.js} +1 -1
  40. django_cfg/static/frontend/admin/_next/static/chunks/50314-79c02212788f1ec7.js +1 -0
  41. django_cfg/static/frontend/admin/_next/static/chunks/{50319.f786248384877960.js → 50319.fd78c7f7e3f1966e.js} +1 -1
  42. django_cfg/static/frontend/admin/_next/static/chunks/{52908.b690e323d8f8efdd.js → 52908.da5b850b0bc0970c.js} +1 -1
  43. django_cfg/static/frontend/admin/_next/static/chunks/{53710.80ca863525d137db.js → 53710.7176bbee6c7b78be.js} +1 -1
  44. django_cfg/static/frontend/admin/_next/static/chunks/{57982.251fed8d58adcf53.js → 57982.2c90b33b0934522a.js} +2 -2
  45. django_cfg/static/frontend/admin/_next/static/chunks/{60181.86e18057c4caaa97.js → 60181.c94d78d10eb5da37.js} +1 -1
  46. django_cfg/static/frontend/admin/_next/static/chunks/{60374.bde0ec1249aa79c6.js → 60374.5d80cfc45439b2b0.js} +1 -1
  47. django_cfg/static/frontend/admin/_next/static/chunks/{6884.7b1db804c88280ed.js → 6884.624d563508cf6db4.js} +1 -1
  48. django_cfg/static/frontend/admin/_next/static/chunks/{69436.9515b854cdf4b57a.js → 69436.be44021e3d7c99c7.js} +1 -1
  49. django_cfg/static/frontend/admin/_next/static/chunks/{70628.00cdd98f672e684f.js → 70628.58e8c38a66543d5e.js} +1 -1
  50. django_cfg/static/frontend/admin/_next/static/chunks/{73218.a826c2248612b37f.js → 73218.d712e7bd678e23a8.js} +1 -1
  51. django_cfg/static/frontend/admin/_next/static/chunks/{76334.64fbaa923d9ac293.js → 76334.f43f2d8b4bbf8dd6.js} +1 -1
  52. django_cfg/static/frontend/admin/_next/static/chunks/{7799.2b280f8ddf067d49.js → 7799.1575cc212bc750c7.js} +1 -1
  53. django_cfg/static/frontend/admin/_next/static/chunks/{80574.620a8a5b4eb91c25.js → 80574.92638dd7b9979664.js} +1 -1
  54. django_cfg/static/frontend/admin/_next/static/chunks/{81127.a0603c3394892d4e.js → 81127.3ead500eec887152.js} +1 -1
  55. django_cfg/static/frontend/admin/_next/static/chunks/82296-a2c8d38f62224be5.js +1 -0
  56. django_cfg/static/frontend/admin/_next/static/chunks/{8383.eb6188b22c453e14.js → 8383.e25a442df26b2e26.js} +1 -1
  57. django_cfg/static/frontend/admin/_next/static/chunks/{85833.35e6ca25ac32a7d2.js → 85833.b0dead4fbcbfdd1b.js} +1 -1
  58. django_cfg/static/frontend/admin/_next/static/chunks/{95365.fc9d7653a78839d0.js → 95365.2b430045fc2e5acf.js} +1 -1
  59. django_cfg/static/frontend/admin/_next/static/chunks/{96424.0793b94836eb13a6.js → 96424.11d76570e9a94b85.js} +1 -1
  60. django_cfg/static/frontend/admin/_next/static/chunks/pages/404-c283223d1afd02a2.js +1 -0
  61. django_cfg/static/frontend/admin/_next/static/chunks/pages/500-389d6d3e1f2f7fda.js +1 -0
  62. django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-f25bec36bbdc9625.js +272 -0
  63. django_cfg/static/frontend/admin/_next/static/chunks/pages/_error-5291033275c26d09.js +1 -0
  64. django_cfg/static/frontend/admin/_next/static/chunks/pages/index-d7bc30185f52cbca.js +1 -0
  65. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-24588bf5551f30df.js → cookies-b39c7f22c066e2c6.js} +1 -1
  66. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-354dae34a4c4da59.js → privacy-5aedad0cf3a4f80f.js} +1 -1
  67. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-0a5d7fa591ebb1ae.js → security-dbd854d0d5d483e2.js} +1 -1
  68. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-c3d80322f52dc112.js → terms-f3e1d2b9e5edf12f.js} +1 -1
  69. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-22532c65971225eb.js +1 -0
  70. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/profile-e93a65e8e7d9022b.js +1 -0
  71. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/ui-669e8f2a785beba2.js +1 -0
  72. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-a8a9ba76f2c75354.js +1 -0
  73. django_cfg/static/frontend/admin/_next/static/chunks/{webpack-905bba30877f6490.js → webpack-92add5f95c66e349.js} +1 -1
  74. django_cfg/static/frontend/admin/_next/static/css/78d677ac1677c210.css +3 -0
  75. django_cfg/static/frontend/admin/auth.html +1 -1
  76. django_cfg/static/frontend/admin/index.html +1 -1
  77. django_cfg/static/frontend/admin/legal/cookies.html +1 -1
  78. django_cfg/static/frontend/admin/legal/privacy.html +1 -1
  79. django_cfg/static/frontend/admin/legal/security.html +1 -1
  80. django_cfg/static/frontend/admin/legal/terms.html +1 -1
  81. django_cfg/static/frontend/admin/private/centrifugo.html +1 -0
  82. django_cfg/static/frontend/admin/private/profile.html +1 -0
  83. django_cfg/static/frontend/admin/private/ui.html +1 -0
  84. django_cfg/static/frontend/admin/private.html +1 -1
  85. django_cfg/templates/admin/index.html +97 -63
  86. django_cfg/templates/admin_old/index.html +80 -0
  87. django_cfg/templatetags/django_cfg.py +57 -10
  88. {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/METADATA +1 -1
  89. {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/RECORD +142 -122
  90. django_cfg/static/frontend/admin/_next/static/chunks/pages/404-7cdad2942c3fb179.js +0 -1
  91. django_cfg/static/frontend/admin/_next/static/chunks/pages/500-6cdb27b00678364f.js +0 -1
  92. django_cfg/static/frontend/admin/_next/static/chunks/pages/_app-9c5ca2471de6b000.js +0 -272
  93. django_cfg/static/frontend/admin/_next/static/chunks/pages/_error-b8071a05cabe1c2d.js +0 -1
  94. django_cfg/static/frontend/admin/_next/static/chunks/pages/index-bf88192a30e013a9.js +0 -1
  95. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-2f58633ddf63a5bc.js +0 -1
  96. django_cfg/static/frontend/admin/_next/static/chunks/pages/ui-73632f2d9c6b11ab.js +0 -1
  97. django_cfg/static/frontend/admin/_next/static/css/e201974f9a4d64e6.css +0 -3
  98. django_cfg/static/frontend/admin/_next/static/qEBrQJUidlI_maQ4xQnI0/_buildManifest.js +0 -1
  99. django_cfg/static/frontend/admin/ui.html +0 -92
  100. /django_cfg/static/frontend/admin/_next/static/{qEBrQJUidlI_maQ4xQnI0 → -Zk0eDB7OJOEFrFyR5BwZ}/_ssgManifest.js +0 -0
  101. /django_cfg/templates/{admin → admin_old}/components/action_grid.html +0 -0
  102. /django_cfg/templates/{admin → admin_old}/components/card.html +0 -0
  103. /django_cfg/templates/{admin → admin_old}/components/data_table.html +0 -0
  104. /django_cfg/templates/{admin → admin_old}/components/metric_card.html +0 -0
  105. /django_cfg/templates/{admin → admin_old}/components/modal.html +0 -0
  106. /django_cfg/templates/{admin → admin_old}/components/progress_bar.html +0 -0
  107. /django_cfg/templates/{admin → admin_old}/components/section_header.html +0 -0
  108. /django_cfg/templates/{admin → admin_old}/components/stat_item.html +0 -0
  109. /django_cfg/templates/{admin → admin_old}/components/stats_grid.html +0 -0
  110. /django_cfg/templates/{admin → admin_old}/components/status_badge.html +0 -0
  111. /django_cfg/templates/{admin → admin_old}/components/user_avatar.html +0 -0
  112. /django_cfg/templates/{admin → admin_old}/constance/change_list.html +0 -0
  113. /django_cfg/templates/{admin → admin_old}/constance/includes/default_value.html +0 -0
  114. /django_cfg/templates/{admin → admin_old}/constance/includes/fieldset_header.html +0 -0
  115. /django_cfg/templates/{admin → admin_old}/constance/includes/results_list.html +0 -0
  116. /django_cfg/templates/{admin → admin_old}/constance/includes/setting_row.html +0 -0
  117. /django_cfg/templates/{admin → admin_old}/constance/includes/table_headers.html +0 -0
  118. /django_cfg/templates/{admin → admin_old}/examples/component_class_example.html +0 -0
  119. /django_cfg/templates/{admin → admin_old}/import_export/change_list_export.html +0 -0
  120. /django_cfg/templates/{admin → admin_old}/import_export/change_list_import.html +0 -0
  121. /django_cfg/templates/{admin → admin_old}/import_export/change_list_import_export.html +0 -0
  122. /django_cfg/templates/{admin → admin_old}/index_new.html +0 -0
  123. /django_cfg/templates/{admin → admin_old}/layouts/base_dashboard.html +0 -0
  124. /django_cfg/templates/{admin → admin_old}/layouts/dashboard_with_tabs.html +0 -0
  125. /django_cfg/templates/{admin → admin_old}/sections/commands_section.html +0 -0
  126. /django_cfg/templates/{admin → admin_old}/sections/documentation_section.html +0 -0
  127. /django_cfg/templates/{admin → admin_old}/sections/overview_section.html +0 -0
  128. /django_cfg/templates/{admin → admin_old}/sections/stats_section.html +0 -0
  129. /django_cfg/templates/{admin → admin_old}/sections/system_section.html +0 -0
  130. /django_cfg/templates/{admin → admin_old}/sections/widgets_section.html +0 -0
  131. /django_cfg/templates/{admin → admin_old}/snippets/components/activity_tracker.html +0 -0
  132. /django_cfg/templates/{admin → admin_old}/snippets/components/charts_section.html +0 -0
  133. /django_cfg/templates/{admin → admin_old}/snippets/components/django_commands.html +0 -0
  134. /django_cfg/templates/{admin → admin_old}/snippets/components/quick_actions.html +0 -0
  135. /django_cfg/templates/{admin → admin_old}/snippets/components/recent_activity_improved.html +0 -0
  136. /django_cfg/templates/{admin → admin_old}/snippets/components/recent_users_table.html +0 -0
  137. /django_cfg/templates/{admin → admin_old}/snippets/components/stats_cards.html +0 -0
  138. /django_cfg/templates/{admin → admin_old}/snippets/components/stats_tiles.html +0 -0
  139. /django_cfg/templates/{admin → admin_old}/snippets/components/system_health.html +0 -0
  140. /django_cfg/templates/{admin → admin_old}/snippets/components/system_metrics.html +0 -0
  141. /django_cfg/templates/{admin → admin_old}/snippets/components/user_permissions.html +0 -0
  142. /django_cfg/templates/{admin → admin_old}/snippets/tabs/app_stats_tab.html +0 -0
  143. /django_cfg/templates/{admin → admin_old}/snippets/tabs/commands_tab.html +0 -0
  144. /django_cfg/templates/{admin → admin_old}/snippets/tabs/documentation_tab.html +0 -0
  145. /django_cfg/templates/{admin → admin_old}/snippets/tabs/overview_tab.html +0 -0
  146. /django_cfg/templates/{admin → admin_old}/snippets/tabs/stats_tab.html +0 -0
  147. /django_cfg/templates/{admin → admin_old}/snippets/tabs/users_tab.html +0 -0
  148. /django_cfg/templates/{admin → admin_old}/snippets/tabs/widgets_tab.html +0 -0
  149. /django_cfg/templates/{admin → admin_old}/snippets/zones/zones_table.html +0 -0
  150. {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/WHEEL +0 -0
  151. {django_cfg-1.4.82.dist-info → django_cfg-1.4.84.dist-info}/entry_points.txt +0 -0
  152. {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)}