django-cfg 1.4.83__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 (133) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/dashboard/__init__.py +8 -0
  3. django_cfg/apps/dashboard/api/__init__.py +27 -0
  4. django_cfg/apps/dashboard/api/serializers.py +165 -0
  5. django_cfg/apps/dashboard/api/viewsets.py +257 -0
  6. django_cfg/apps/dashboard/apps.py +23 -0
  7. django_cfg/apps/dashboard/services/__init__.py +11 -0
  8. django_cfg/apps/dashboard/services/statistics_service.py +235 -0
  9. django_cfg/apps/dashboard/services/system_health_service.py +280 -0
  10. django_cfg/apps/dashboard/urls.py +23 -0
  11. django_cfg/apps/frontend/views.py +5 -0
  12. django_cfg/apps/urls.py +2 -1
  13. django_cfg/core/builders/apps_builder.py +1 -0
  14. django_cfg/modules/django_client/core/parser/openapi30.py +12 -5
  15. django_cfg/modules/django_client/core/parser/openapi31.py +12 -5
  16. django_cfg/pyproject.toml +1 -1
  17. django_cfg/static/frontend/admin/404.html +1 -1
  18. django_cfg/static/frontend/admin/500.html +1 -1
  19. django_cfg/static/frontend/admin/_next/static/-Zk0eDB7OJOEFrFyR5BwZ/_buildManifest.js +1 -0
  20. django_cfg/static/frontend/admin/_next/static/chunks/{43076.55dd23b6cd68edb0.js → 20695.a7d37b6c40ad3f58.js} +1 -1
  21. django_cfg/static/frontend/admin/_next/static/chunks/{25033.d626f78bc99bc4a1.js → 25033.ee3e206d5a2877b6.js} +2 -2
  22. django_cfg/static/frontend/admin/_next/static/chunks/{25892.964150a58f94ce06.js → 25892.5cbed319f9226fdc.js} +1 -1
  23. django_cfg/static/frontend/admin/_next/static/chunks/{2d7a934f.dfef67639279d59d.js → 2d7a934f.329c61f23af1a7ec.js} +1 -1
  24. django_cfg/static/frontend/admin/_next/static/chunks/{30649.00c679812a56aee3.js → 30649.963cfb7268b5864a.js} +1 -1
  25. django_cfg/static/frontend/admin/_next/static/chunks/{30875.784491146c38dbcb.js → 30875.82c3741757b8aa32.js} +1 -1
  26. django_cfg/static/frontend/admin/_next/static/chunks/{32163.ab0ca435b3f26c04.js → 32163.109a03a7252f1508.js} +1 -1
  27. django_cfg/static/frontend/admin/_next/static/chunks/43076-4be6a9794e9c3e8b.js +1 -0
  28. django_cfg/static/frontend/admin/_next/static/chunks/{49978.fb8ba7ee52ffe666.js → 49978.db5a86a8eb233f35.js} +1 -1
  29. django_cfg/static/frontend/admin/_next/static/chunks/50314-79c02212788f1ec7.js +1 -0
  30. django_cfg/static/frontend/admin/_next/static/chunks/{50319.f786248384877960.js → 50319.fd78c7f7e3f1966e.js} +1 -1
  31. django_cfg/static/frontend/admin/_next/static/chunks/{52908.b690e323d8f8efdd.js → 52908.da5b850b0bc0970c.js} +1 -1
  32. django_cfg/static/frontend/admin/_next/static/chunks/{53710.80ca863525d137db.js → 53710.7176bbee6c7b78be.js} +1 -1
  33. django_cfg/static/frontend/admin/_next/static/chunks/{57982.251fed8d58adcf53.js → 57982.2c90b33b0934522a.js} +2 -2
  34. django_cfg/static/frontend/admin/_next/static/chunks/{60181.86e18057c4caaa97.js → 60181.c94d78d10eb5da37.js} +1 -1
  35. django_cfg/static/frontend/admin/_next/static/chunks/{60374.bde0ec1249aa79c6.js → 60374.5d80cfc45439b2b0.js} +1 -1
  36. django_cfg/static/frontend/admin/_next/static/chunks/{6884.7b1db804c88280ed.js → 6884.624d563508cf6db4.js} +1 -1
  37. django_cfg/static/frontend/admin/_next/static/chunks/{69436.9515b854cdf4b57a.js → 69436.be44021e3d7c99c7.js} +1 -1
  38. django_cfg/static/frontend/admin/_next/static/chunks/{70628.00cdd98f672e684f.js → 70628.58e8c38a66543d5e.js} +1 -1
  39. django_cfg/static/frontend/admin/_next/static/chunks/{73218.a826c2248612b37f.js → 73218.d712e7bd678e23a8.js} +1 -1
  40. django_cfg/static/frontend/admin/_next/static/chunks/{76334.64fbaa923d9ac293.js → 76334.f43f2d8b4bbf8dd6.js} +1 -1
  41. django_cfg/static/frontend/admin/_next/static/chunks/{7799.2b280f8ddf067d49.js → 7799.1575cc212bc750c7.js} +1 -1
  42. django_cfg/static/frontend/admin/_next/static/chunks/{80574.620a8a5b4eb91c25.js → 80574.92638dd7b9979664.js} +1 -1
  43. django_cfg/static/frontend/admin/_next/static/chunks/{81127.a0603c3394892d4e.js → 81127.3ead500eec887152.js} +1 -1
  44. django_cfg/static/frontend/admin/_next/static/chunks/82296-a2c8d38f62224be5.js +1 -0
  45. django_cfg/static/frontend/admin/_next/static/chunks/{8383.eb6188b22c453e14.js → 8383.e25a442df26b2e26.js} +1 -1
  46. django_cfg/static/frontend/admin/_next/static/chunks/{85833.35e6ca25ac32a7d2.js → 85833.b0dead4fbcbfdd1b.js} +1 -1
  47. django_cfg/static/frontend/admin/_next/static/chunks/{95365.fc9d7653a78839d0.js → 95365.2b430045fc2e5acf.js} +1 -1
  48. django_cfg/static/frontend/admin/_next/static/chunks/{96424.0793b94836eb13a6.js → 96424.11d76570e9a94b85.js} +1 -1
  49. django_cfg/static/frontend/admin/_next/static/chunks/pages/{_app-16701a4e1bc3e6ac.js → _app-f25bec36bbdc9625.js} +46 -46
  50. django_cfg/static/frontend/admin/_next/static/chunks/pages/index-d7bc30185f52cbca.js +1 -0
  51. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-bb5507a122775f30.js → cookies-b39c7f22c066e2c6.js} +1 -1
  52. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-f8a3d8db1a197be3.js → privacy-5aedad0cf3a4f80f.js} +1 -1
  53. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-aba50addd2179f8f.js → security-dbd854d0d5d483e2.js} +1 -1
  54. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-4aa35cd30b5c08ad.js → terms-f3e1d2b9e5edf12f.js} +1 -1
  55. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-22532c65971225eb.js +1 -0
  56. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/ui-669e8f2a785beba2.js +1 -0
  57. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-a8a9ba76f2c75354.js +1 -0
  58. django_cfg/static/frontend/admin/_next/static/chunks/{webpack-905bba30877f6490.js → webpack-92add5f95c66e349.js} +1 -1
  59. django_cfg/static/frontend/admin/_next/static/css/78d677ac1677c210.css +3 -0
  60. django_cfg/static/frontend/admin/auth.html +1 -1
  61. django_cfg/static/frontend/admin/index.html +1 -1
  62. django_cfg/static/frontend/admin/legal/cookies.html +1 -1
  63. django_cfg/static/frontend/admin/legal/privacy.html +1 -1
  64. django_cfg/static/frontend/admin/legal/security.html +1 -1
  65. django_cfg/static/frontend/admin/legal/terms.html +1 -1
  66. django_cfg/static/frontend/admin/private/centrifugo.html +1 -1
  67. django_cfg/static/frontend/admin/private/profile.html +1 -1
  68. django_cfg/static/frontend/admin/private/ui.html +1 -0
  69. django_cfg/static/frontend/admin/private.html +1 -1
  70. django_cfg/templates/admin/index.html +97 -63
  71. django_cfg/templates/admin_old/index.html +80 -0
  72. {django_cfg-1.4.83.dist-info → django_cfg-1.4.84.dist-info}/METADATA +1 -1
  73. {django_cfg-1.4.83.dist-info → django_cfg-1.4.84.dist-info}/RECORD +126 -113
  74. django_cfg/static/frontend/admin/_next/static/chunks/pages/index-88751d9f44a32105.js +0 -1
  75. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/centrifugo-1c5f00c26c77a47b.js +0 -1
  76. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-2f58633ddf63a5bc.js +0 -1
  77. django_cfg/static/frontend/admin/_next/static/chunks/pages/ui-0e6c0e35862789ec.js +0 -1
  78. django_cfg/static/frontend/admin/_next/static/css/806300fb98c42afb.css +0 -3
  79. django_cfg/static/frontend/admin/_next/static/ibMHm1p66p0UGKsKnDWxn/_buildManifest.js +0 -1
  80. django_cfg/static/frontend/admin/ui.html +0 -92
  81. /django_cfg/static/frontend/admin/_next/static/{ibMHm1p66p0UGKsKnDWxn → -Zk0eDB7OJOEFrFyR5BwZ}/_ssgManifest.js +0 -0
  82. /django_cfg/templates/{admin → admin_old}/components/action_grid.html +0 -0
  83. /django_cfg/templates/{admin → admin_old}/components/card.html +0 -0
  84. /django_cfg/templates/{admin → admin_old}/components/data_table.html +0 -0
  85. /django_cfg/templates/{admin → admin_old}/components/metric_card.html +0 -0
  86. /django_cfg/templates/{admin → admin_old}/components/modal.html +0 -0
  87. /django_cfg/templates/{admin → admin_old}/components/progress_bar.html +0 -0
  88. /django_cfg/templates/{admin → admin_old}/components/section_header.html +0 -0
  89. /django_cfg/templates/{admin → admin_old}/components/stat_item.html +0 -0
  90. /django_cfg/templates/{admin → admin_old}/components/stats_grid.html +0 -0
  91. /django_cfg/templates/{admin → admin_old}/components/status_badge.html +0 -0
  92. /django_cfg/templates/{admin → admin_old}/components/user_avatar.html +0 -0
  93. /django_cfg/templates/{admin → admin_old}/constance/change_list.html +0 -0
  94. /django_cfg/templates/{admin → admin_old}/constance/includes/default_value.html +0 -0
  95. /django_cfg/templates/{admin → admin_old}/constance/includes/fieldset_header.html +0 -0
  96. /django_cfg/templates/{admin → admin_old}/constance/includes/results_list.html +0 -0
  97. /django_cfg/templates/{admin → admin_old}/constance/includes/setting_row.html +0 -0
  98. /django_cfg/templates/{admin → admin_old}/constance/includes/table_headers.html +0 -0
  99. /django_cfg/templates/{admin → admin_old}/examples/component_class_example.html +0 -0
  100. /django_cfg/templates/{admin → admin_old}/import_export/change_list_export.html +0 -0
  101. /django_cfg/templates/{admin → admin_old}/import_export/change_list_import.html +0 -0
  102. /django_cfg/templates/{admin → admin_old}/import_export/change_list_import_export.html +0 -0
  103. /django_cfg/templates/{admin → admin_old}/index_new.html +0 -0
  104. /django_cfg/templates/{admin → admin_old}/layouts/base_dashboard.html +0 -0
  105. /django_cfg/templates/{admin → admin_old}/layouts/dashboard_with_tabs.html +0 -0
  106. /django_cfg/templates/{admin → admin_old}/sections/commands_section.html +0 -0
  107. /django_cfg/templates/{admin → admin_old}/sections/documentation_section.html +0 -0
  108. /django_cfg/templates/{admin → admin_old}/sections/overview_section.html +0 -0
  109. /django_cfg/templates/{admin → admin_old}/sections/stats_section.html +0 -0
  110. /django_cfg/templates/{admin → admin_old}/sections/system_section.html +0 -0
  111. /django_cfg/templates/{admin → admin_old}/sections/widgets_section.html +0 -0
  112. /django_cfg/templates/{admin → admin_old}/snippets/components/activity_tracker.html +0 -0
  113. /django_cfg/templates/{admin → admin_old}/snippets/components/charts_section.html +0 -0
  114. /django_cfg/templates/{admin → admin_old}/snippets/components/django_commands.html +0 -0
  115. /django_cfg/templates/{admin → admin_old}/snippets/components/quick_actions.html +0 -0
  116. /django_cfg/templates/{admin → admin_old}/snippets/components/recent_activity_improved.html +0 -0
  117. /django_cfg/templates/{admin → admin_old}/snippets/components/recent_users_table.html +0 -0
  118. /django_cfg/templates/{admin → admin_old}/snippets/components/stats_cards.html +0 -0
  119. /django_cfg/templates/{admin → admin_old}/snippets/components/stats_tiles.html +0 -0
  120. /django_cfg/templates/{admin → admin_old}/snippets/components/system_health.html +0 -0
  121. /django_cfg/templates/{admin → admin_old}/snippets/components/system_metrics.html +0 -0
  122. /django_cfg/templates/{admin → admin_old}/snippets/components/user_permissions.html +0 -0
  123. /django_cfg/templates/{admin → admin_old}/snippets/tabs/app_stats_tab.html +0 -0
  124. /django_cfg/templates/{admin → admin_old}/snippets/tabs/commands_tab.html +0 -0
  125. /django_cfg/templates/{admin → admin_old}/snippets/tabs/documentation_tab.html +0 -0
  126. /django_cfg/templates/{admin → admin_old}/snippets/tabs/overview_tab.html +0 -0
  127. /django_cfg/templates/{admin → admin_old}/snippets/tabs/stats_tab.html +0 -0
  128. /django_cfg/templates/{admin → admin_old}/snippets/tabs/users_tab.html +0 -0
  129. /django_cfg/templates/{admin → admin_old}/snippets/tabs/widgets_tab.html +0 -0
  130. /django_cfg/templates/{admin → admin_old}/snippets/zones/zones_table.html +0 -0
  131. {django_cfg-1.4.83.dist-info → django_cfg-1.4.84.dist-info}/WHEEL +0 -0
  132. {django_cfg-1.4.83.dist-info → django_cfg-1.4.84.dist-info}/entry_points.txt +0 -0
  133. {django_cfg-1.4.83.dist-info → django_cfg-1.4.84.dist-info}/licenses/LICENSE +0 -0
@@ -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)}
@@ -0,0 +1,280 @@
1
+ """
2
+ System Health Service
3
+
4
+ Monitors system components health status.
5
+ Checks database, cache, queue, storage, and API availability.
6
+ """
7
+
8
+ import logging
9
+ from datetime import datetime
10
+ from typing import Any, Dict, List, Literal
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class SystemHealthService:
16
+ """
17
+ Service for monitoring system component health.
18
+
19
+ %%PRIORITY:HIGH%%
20
+ %%AI_HINT: Checks health of various system components%%
21
+
22
+ TAGS: health, monitoring, system, service
23
+ DEPENDS_ON: [django.db.connection, django.core.cache, redis]
24
+ """
25
+
26
+ def __init__(self):
27
+ """Initialize system health service."""
28
+ self.logger = logger
29
+
30
+ def check_database_health(self) -> Dict[str, Any]:
31
+ """
32
+ Check database connectivity and health.
33
+
34
+ Returns:
35
+ Health status dictionary with status, description, last_check
36
+ """
37
+ try:
38
+ from django.db import connection
39
+
40
+ # Test database connection
41
+ with connection.cursor() as cursor:
42
+ cursor.execute("SELECT 1")
43
+ cursor.fetchone()
44
+
45
+ return {
46
+ 'component': 'database',
47
+ 'status': 'healthy',
48
+ 'description': 'Database connection is working',
49
+ 'last_check': datetime.now().isoformat(),
50
+ 'health_percentage': 100,
51
+ }
52
+ except Exception as e:
53
+ self.logger.error(f"Database health check failed: {e}")
54
+ return {
55
+ 'component': 'database',
56
+ 'status': 'error',
57
+ 'description': f'Database error: {str(e)}',
58
+ 'last_check': datetime.now().isoformat(),
59
+ 'health_percentage': 0,
60
+ }
61
+
62
+ def check_cache_health(self) -> Dict[str, Any]:
63
+ """
64
+ Check cache (Redis/Memcached) connectivity and health.
65
+
66
+ Returns:
67
+ Health status dictionary
68
+ """
69
+ try:
70
+ from django.core.cache import cache
71
+
72
+ # Test cache by setting and getting a test value
73
+ test_key = 'health_check_test'
74
+ test_value = 'ok'
75
+ cache.set(test_key, test_value, timeout=10)
76
+ result = cache.get(test_key)
77
+
78
+ if result == test_value:
79
+ cache.delete(test_key)
80
+ return {
81
+ 'component': 'cache',
82
+ 'status': 'healthy',
83
+ 'description': 'Cache is working correctly',
84
+ 'last_check': datetime.now().isoformat(),
85
+ 'health_percentage': 100,
86
+ }
87
+ else:
88
+ return {
89
+ 'component': 'cache',
90
+ 'status': 'warning',
91
+ 'description': 'Cache test failed',
92
+ 'last_check': datetime.now().isoformat(),
93
+ 'health_percentage': 50,
94
+ }
95
+
96
+ except Exception as e:
97
+ self.logger.error(f"Cache health check failed: {e}")
98
+ return {
99
+ 'component': 'cache',
100
+ 'status': 'error',
101
+ 'description': f'Cache error: {str(e)}',
102
+ 'last_check': datetime.now().isoformat(),
103
+ 'health_percentage': 0,
104
+ }
105
+
106
+ def check_queue_health(self) -> Dict[str, Any]:
107
+ """
108
+ Check task queue (Celery/Dramatiq) health.
109
+
110
+ Returns:
111
+ Health status dictionary
112
+ """
113
+ try:
114
+ # TODO: Add real queue health check
115
+ # Example: Check Redis connection, queue sizes, worker status
116
+ from django_cfg.modules.django_tasks import DjangoTasks
117
+
118
+ tasks = DjangoTasks()
119
+ redis_client = tasks.get_redis_client()
120
+
121
+ if redis_client and redis_client.ping():
122
+ return {
123
+ 'component': 'queue',
124
+ 'status': 'healthy',
125
+ 'description': 'Queue system is operational',
126
+ 'last_check': datetime.now().isoformat(),
127
+ 'health_percentage': 100,
128
+ }
129
+ else:
130
+ return {
131
+ 'component': 'queue',
132
+ 'status': 'error',
133
+ 'description': 'Queue system unavailable',
134
+ 'last_check': datetime.now().isoformat(),
135
+ 'health_percentage': 0,
136
+ }
137
+
138
+ except Exception as e:
139
+ self.logger.error(f"Queue health check failed: {e}")
140
+ return {
141
+ 'component': 'queue',
142
+ 'status': 'error',
143
+ 'description': f'Queue error: {str(e)}',
144
+ 'last_check': datetime.now().isoformat(),
145
+ 'health_percentage': 0,
146
+ }
147
+
148
+ def check_storage_health(self) -> Dict[str, Any]:
149
+ """
150
+ Check storage/file system health.
151
+
152
+ Returns:
153
+ Health status dictionary
154
+ """
155
+ try:
156
+ import os
157
+ from django.conf import settings
158
+
159
+ # Check if media directory is writable
160
+ media_root = getattr(settings, 'MEDIA_ROOT', None)
161
+
162
+ if media_root and os.path.exists(media_root) and os.access(media_root, os.W_OK):
163
+ return {
164
+ 'component': 'storage',
165
+ 'status': 'healthy',
166
+ 'description': 'Storage is accessible and writable',
167
+ 'last_check': datetime.now().isoformat(),
168
+ 'health_percentage': 100,
169
+ }
170
+ else:
171
+ return {
172
+ 'component': 'storage',
173
+ 'status': 'warning',
174
+ 'description': 'Storage may have limited access',
175
+ 'last_check': datetime.now().isoformat(),
176
+ 'health_percentage': 70,
177
+ }
178
+
179
+ except Exception as e:
180
+ self.logger.error(f"Storage health check failed: {e}")
181
+ return {
182
+ 'component': 'storage',
183
+ 'status': 'error',
184
+ 'description': f'Storage error: {str(e)}',
185
+ 'last_check': datetime.now().isoformat(),
186
+ 'health_percentage': 0,
187
+ }
188
+
189
+ def get_all_health_checks(self) -> List[Dict[str, Any]]:
190
+ """
191
+ Run all health checks and return aggregated results.
192
+
193
+ Returns:
194
+ List of health check results for all components
195
+
196
+ USED_BY: DashboardViewSet.system_health endpoint
197
+ """
198
+ checks = [
199
+ self.check_database_health(),
200
+ self.check_cache_health(),
201
+ self.check_queue_health(),
202
+ self.check_storage_health(),
203
+ ]
204
+
205
+ return checks
206
+
207
+ def get_overall_health_status(self) -> Dict[str, Any]:
208
+ """
209
+ Get overall system health status.
210
+
211
+ Returns:
212
+ Dictionary with overall status, percentage, and component details
213
+ """
214
+ checks = self.get_all_health_checks()
215
+
216
+ # Calculate overall health percentage
217
+ total_health = sum(check.get('health_percentage', 0) for check in checks)
218
+ overall_percentage = total_health // len(checks) if checks else 0
219
+
220
+ # Determine overall status
221
+ statuses = [check.get('status') for check in checks]
222
+ if 'error' in statuses:
223
+ overall_status = 'error'
224
+ elif 'warning' in statuses:
225
+ overall_status = 'warning'
226
+ else:
227
+ overall_status = 'healthy'
228
+
229
+ return {
230
+ 'overall_status': overall_status,
231
+ 'overall_health_percentage': overall_percentage,
232
+ 'components': checks,
233
+ 'timestamp': datetime.now().isoformat(),
234
+ }
235
+
236
+ def get_quick_actions(self) -> List[Dict[str, Any]]:
237
+ """
238
+ Get quick action buttons for dashboard.
239
+
240
+ Returns:
241
+ List of quick action dictionaries
242
+
243
+ %%AI_HINT: Actions link to admin pages or trigger common tasks%%
244
+ """
245
+ actions = [
246
+ {
247
+ 'title': 'User Management',
248
+ 'description': 'Manage users and permissions',
249
+ 'icon': 'people',
250
+ 'link': '/admin/auth/user/',
251
+ 'color': 'primary',
252
+ 'category': 'admin',
253
+ },
254
+ {
255
+ 'title': 'View Logs',
256
+ 'description': 'Check system logs',
257
+ 'icon': 'description',
258
+ 'link': '/admin/django_cfg/logs/',
259
+ 'color': 'secondary',
260
+ 'category': 'system',
261
+ },
262
+ {
263
+ 'title': 'Clear Cache',
264
+ 'description': 'Clear application cache',
265
+ 'icon': 'refresh',
266
+ 'link': '/cfg/admin/cache/clear/',
267
+ 'color': 'warning',
268
+ 'category': 'system',
269
+ },
270
+ {
271
+ 'title': 'Run Backup',
272
+ 'description': 'Create system backup',
273
+ 'icon': 'backup',
274
+ 'link': '/cfg/admin/backup/create/',
275
+ 'color': 'success',
276
+ 'category': 'system',
277
+ },
278
+ ]
279
+
280
+ return actions
@@ -0,0 +1,23 @@
1
+ """
2
+ Dashboard URLs
3
+
4
+ RESTful API endpoints for dashboard data.
5
+ Follows the same pattern as tasks app.
6
+ """
7
+
8
+ from django.urls import include, path
9
+ from rest_framework.routers import DefaultRouter
10
+
11
+ from .api.viewsets import DashboardViewSet
12
+
13
+ app_name = 'dashboard'
14
+
15
+ # Main router for ViewSets
16
+ router = DefaultRouter()
17
+ router.register(r'', DashboardViewSet, basename='dashboard')
18
+
19
+ urlpatterns = [
20
+ # RESTful API endpoints using ViewSets
21
+ # Mounted at cfg/dashboard/api/
22
+ path('api/', include(router.urls)),
23
+ ]
@@ -40,6 +40,11 @@ class NextJSStaticView(View):
40
40
  if not path or path == '/':
41
41
  path = 'index.html'
42
42
 
43
+ # Handle trailing slash (Next.js static export behavior)
44
+ # /private/ -> private.html
45
+ if path.endswith('/') and path != '/':
46
+ path = path.rstrip('/') + '.html'
47
+
43
48
  # For routes without extension, try .html (Next.js static export behavior)
44
49
  file_path = base_dir / path
45
50
  if not file_path.exists() and not path.endswith('.html') and '.' not in Path(path).name:
django_cfg/apps/urls.py CHANGED
@@ -84,7 +84,7 @@ def get_default_cfg_group():
84
84
  name="cfg",
85
85
  apps=get_enabled_cfg_apps(),
86
86
  title="Django-CFG API",
87
- description="Authentication (OTP), Support, Newsletter, Leads, Knowledge Base, AI Agents, Tasks, Payments",
87
+ description="Authentication (OTP), Support, Newsletter, Leads, Knowledge Base, AI Agents, Tasks, Payments, Dashboard",
88
88
  version="1.0.0",
89
89
  )
90
90
 
@@ -134,6 +134,7 @@ urlpatterns = [
134
134
  path('cfg/commands/', include('django_cfg.apps.api.commands.urls')),
135
135
  path('cfg/openapi/', include('django_cfg.modules.django_client.urls')),
136
136
  path('cfg/admin/', include('django_cfg.apps.frontend.urls')), # Next.js admin panel
137
+ path('cfg/dashboard/', include('django_cfg.apps.dashboard.urls')), # Dashboard API
137
138
  ]
138
139
 
139
140
  # Django-CFG apps - conditionally registered based on config
@@ -109,6 +109,7 @@ class InstalledAppsBuilder:
109
109
  "django_cfg.modules.django_tailwind", # Universal Tailwind layouts
110
110
  "django_cfg.apps.api.health",
111
111
  "django_cfg.apps.api.commands",
112
+ "django_cfg.apps.dashboard", # Dashboard API
112
113
  ]
113
114
 
114
115
  if self.config.enable_frontend:
@@ -59,16 +59,23 @@ class OpenAPI30Parser(BaseParser):
59
59
  return True
60
60
 
61
61
  # Check anyOf: [{"type": "X"}, {"type": "null"}] format (Pydantic)
62
+ # or anyOf: [{"$ref": "..."}, {"type": "null"}] format
62
63
  if schema.anyOf and len(schema.anyOf) == 2:
63
- types = []
64
+ has_null = False
65
+ has_actual_type = False
66
+
64
67
  for item in schema.anyOf:
65
68
  if not isinstance(item, SchemaObject):
66
69
  continue
67
- if item.base_type:
68
- types.append(item.base_type)
69
70
 
70
- # If one type is 'null' and another is actual type, it's nullable
71
- if 'null' in types and len(types) == 2:
71
+ if item.base_type == 'null':
72
+ has_null = True
73
+ elif item.base_type or item.ref:
74
+ # Has actual type (either base_type or $ref)
75
+ has_actual_type = True
76
+
77
+ # If one is null and another is actual type, it's nullable
78
+ if has_null and has_actual_type:
72
79
  return True
73
80
 
74
81
  return False
@@ -67,16 +67,23 @@ class OpenAPI31Parser(BaseParser):
67
67
  return True
68
68
 
69
69
  # Check anyOf: [{"type": "X"}, {"type": "null"}] format (Pydantic)
70
+ # or anyOf: [{"$ref": "..."}, {"type": "null"}] format
70
71
  if schema.anyOf and len(schema.anyOf) == 2:
71
- types = []
72
+ has_null = False
73
+ has_actual_type = False
74
+
72
75
  for item in schema.anyOf:
73
76
  if not isinstance(item, SchemaObject):
74
77
  continue
75
- if item.base_type:
76
- types.append(item.base_type)
77
78
 
78
- # If one type is 'null' and another is actual type, it's nullable
79
- if 'null' in types and len(types) == 2:
79
+ if item.base_type == 'null':
80
+ has_null = True
81
+ elif item.base_type or item.ref:
82
+ # Has actual type (either base_type or $ref)
83
+ has_actual_type = True
84
+
85
+ # If one is null and another is actual type, it's nullable
86
+ if has_null and has_actual_type:
80
87
  return True
81
88
 
82
89
  return False
django_cfg/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.4.83"
7
+ version = "1.4.84"
8
8
  description = "Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "ai-agents", "enterprise-django", "django-settings", "type-safe-config",]