django-cfg 1.4.87__py3-none-any.whl → 1.4.89__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 (177) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/centrifugo/views/__init__.py +0 -2
  3. django_cfg/apps/dashboard/permissions.py +48 -0
  4. django_cfg/apps/dashboard/serializers/__init__.py +8 -1
  5. django_cfg/apps/dashboard/serializers/commands.py +29 -0
  6. django_cfg/apps/dashboard/services/__init__.py +2 -0
  7. django_cfg/{modules/django_unfold/callbacks/base.py → apps/dashboard/services/commands_security.py} +28 -90
  8. django_cfg/apps/dashboard/services/commands_service.py +208 -9
  9. django_cfg/apps/dashboard/services/overview_service.py +205 -0
  10. django_cfg/apps/dashboard/views/commands_views.py +92 -4
  11. django_cfg/apps/frontend/test_routing.py +134 -0
  12. django_cfg/apps/frontend/views.py +73 -28
  13. django_cfg/apps/urls.py +0 -1
  14. django_cfg/core/builders/apps_builder.py +0 -58
  15. django_cfg/modules/django_unfold/__init__.py +5 -24
  16. django_cfg/modules/django_unfold/models/__init__.py +0 -23
  17. django_cfg/modules/django_unfold/models/config.py +11 -65
  18. django_cfg/modules/django_unfold/{dashboard.py → navigation.py} +21 -152
  19. django_cfg/modules/django_unfold/tailwind.py +2 -4
  20. django_cfg/pyproject.toml +1 -1
  21. django_cfg/registry/third_party.py +0 -9
  22. django_cfg/routing/callbacks.py +1 -43
  23. django_cfg/static/frontend/admin/404/index.html +1 -1
  24. django_cfg/static/frontend/admin/404.html +1 -1
  25. django_cfg/static/frontend/admin/500/index.html +1 -1
  26. django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_buildManifest.js +1 -1
  27. django_cfg/static/frontend/admin/_next/static/chunks/{19430.fe7bff7372f8a256.js → 19430.c4c95603c23c17fe.js} +1 -1
  28. django_cfg/static/frontend/admin/_next/static/chunks/50314-9443faa6df24aebf.js +1 -0
  29. django_cfg/static/frontend/admin/_next/static/chunks/94141-bc6d47f419b26b21.js +1 -0
  30. django_cfg/static/frontend/admin/_next/static/chunks/pages/{_app-c336f254967dd101.js → _app-c7dcd3aa616fab68.js} +6 -6
  31. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-b39c7f22c066e2c6.js → cookies-97d279800f12aab4.js} +1 -1
  32. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-5aedad0cf3a4f80f.js → privacy-1d5e6cd94689247e.js} +1 -1
  33. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-dbd854d0d5d483e2.js → security-55e49700e7a01f5a.js} +1 -1
  34. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-f3e1d2b9e5edf12f.js → terms-14c02bb2d3198352.js} +1 -1
  35. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{centrifugo-22532c65971225eb.js → centrifugo-f9ecbc3ae0052a03.js} +1 -1
  36. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-d4ccbe1265cbd853.js +1 -0
  37. django_cfg/static/frontend/admin/_next/static/chunks/{webpack-da114020a6b940f5.js → webpack-5a92f81363b62aa7.js} +1 -1
  38. django_cfg/static/frontend/admin/_next/static/css/3063068f0d5a8a00.css +3 -0
  39. django_cfg/static/frontend/admin/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  40. django_cfg/static/frontend/admin/_next/static/media/21350d82a1f187e9-s.p.woff2 +0 -0
  41. django_cfg/static/frontend/admin/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  42. django_cfg/static/frontend/admin/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  43. django_cfg/static/frontend/admin/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  44. django_cfg/static/frontend/admin/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  45. django_cfg/static/frontend/admin/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  46. django_cfg/static/frontend/admin/auth/index.html +1 -1
  47. django_cfg/static/frontend/admin/index.html +1 -1
  48. django_cfg/static/frontend/admin/legal/cookies/index.html +1 -1
  49. django_cfg/static/frontend/admin/legal/privacy/index.html +1 -1
  50. django_cfg/static/frontend/admin/legal/security/index.html +1 -1
  51. django_cfg/static/frontend/admin/legal/terms/index.html +1 -1
  52. django_cfg/static/frontend/admin/private/centrifugo/index.html +1 -1
  53. django_cfg/static/frontend/admin/private/index.html +1 -1
  54. django_cfg/static/frontend/admin/private/profile/index.html +1 -1
  55. django_cfg/static/frontend/admin/private/ui/index.html +2 -2
  56. django_cfg/templates/admin/index.html +1 -1
  57. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/METADATA +1 -1
  58. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/RECORD +62 -163
  59. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +0 -260
  60. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +0 -313
  61. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +0 -803
  62. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +0 -341
  63. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +0 -432
  64. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +0 -33
  65. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +0 -210
  66. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +0 -46
  67. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +0 -123
  68. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +0 -45
  69. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +0 -84
  70. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/stat_cards.html +0 -53
  71. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +0 -91
  72. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/tab_navigation.html +0 -29
  73. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +0 -415
  74. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +0 -61
  75. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +0 -58
  76. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +0 -48
  77. django_cfg/apps/centrifugo/templatetags/__init__.py +0 -1
  78. django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +0 -81
  79. django_cfg/apps/centrifugo/urls_admin.py +0 -20
  80. django_cfg/apps/centrifugo/views/dashboard.py +0 -28
  81. django_cfg/modules/django_dashboard/__init__.py +0 -23
  82. django_cfg/modules/django_dashboard/components.py +0 -312
  83. django_cfg/modules/django_dashboard/debug.py +0 -174
  84. django_cfg/modules/django_dashboard/management/__init__.py +0 -0
  85. django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
  86. django_cfg/modules/django_dashboard/management/commands/debug_dashboard.py +0 -109
  87. django_cfg/modules/django_dashboard/sections/__init__.py +0 -1
  88. django_cfg/modules/django_dashboard/sections/base.py +0 -129
  89. django_cfg/modules/django_dashboard/sections/commands.py +0 -33
  90. django_cfg/modules/django_dashboard/sections/documentation.py +0 -393
  91. django_cfg/modules/django_dashboard/sections/overview.py +0 -398
  92. django_cfg/modules/django_dashboard/sections/stats.py +0 -48
  93. django_cfg/modules/django_dashboard/sections/system.py +0 -74
  94. django_cfg/modules/django_dashboard/sections/widgets.py +0 -222
  95. django_cfg/modules/django_unfold/callbacks/__init__.py +0 -9
  96. django_cfg/modules/django_unfold/callbacks/actions.py +0 -51
  97. django_cfg/modules/django_unfold/callbacks/apizones.py +0 -122
  98. django_cfg/modules/django_unfold/callbacks/charts.py +0 -223
  99. django_cfg/modules/django_unfold/callbacks/commands.py +0 -40
  100. django_cfg/modules/django_unfold/callbacks/main.py +0 -322
  101. django_cfg/modules/django_unfold/callbacks/statistics.py +0 -240
  102. django_cfg/modules/django_unfold/callbacks/system.py +0 -180
  103. django_cfg/modules/django_unfold/callbacks/users.py +0 -65
  104. django_cfg/modules/django_unfold/models/dashboard.py +0 -207
  105. django_cfg/modules/django_unfold/models/tabs.py +0 -26
  106. django_cfg/modules/django_unfold/models.py +0 -98
  107. django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +0 -102
  108. django_cfg/static/frontend/admin/_next/static/chunks/23004-faae121bbfecc163.js +0 -1
  109. django_cfg/static/frontend/admin/_next/static/chunks/50314-48bd5701f62faf27.js +0 -1
  110. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-fe9faa86ecdb0ce6.js +0 -1
  111. django_cfg/static/frontend/admin/_next/static/css/5f9a37b6e6a72303.css +0 -3
  112. django_cfg/static/frontend/admin/_next/static/media/438aa629764e75f3-s.woff2 +0 -0
  113. django_cfg/static/frontend/admin/_next/static/media/4c9affa5bc8f420e-s.p.woff2 +0 -0
  114. django_cfg/static/frontend/admin/_next/static/media/51251f8b9793cdb3-s.woff2 +0 -0
  115. django_cfg/static/frontend/admin/_next/static/media/875ae681bfde4580-s.p.woff2 +0 -0
  116. django_cfg/static/frontend/admin/_next/static/media/cc978ac5ee68c2b6-s.woff2 +0 -0
  117. django_cfg/static/frontend/admin/_next/static/media/e857b654a2caa584-s.woff2 +0 -0
  118. django_cfg/templates/admin/sections/commands_section.html +0 -5
  119. django_cfg/templates/admin/sections/documentation_section.html +0 -5
  120. django_cfg/templates/admin/sections/overview_section.html +0 -5
  121. django_cfg/templates/admin/sections/stats_section.html +0 -5
  122. django_cfg/templates/admin/sections/system_section.html +0 -5
  123. django_cfg/templates/admin/sections/widgets_section.html +0 -11
  124. django_cfg/templates/admin_old/components/action_grid.html +0 -49
  125. django_cfg/templates/admin_old/components/card.html +0 -50
  126. django_cfg/templates/admin_old/components/data_table.html +0 -67
  127. django_cfg/templates/admin_old/components/metric_card.html +0 -39
  128. django_cfg/templates/admin_old/components/modal.html +0 -58
  129. django_cfg/templates/admin_old/components/progress_bar.html +0 -20
  130. django_cfg/templates/admin_old/components/section_header.html +0 -26
  131. django_cfg/templates/admin_old/components/stat_item.html +0 -32
  132. django_cfg/templates/admin_old/components/stats_grid.html +0 -72
  133. django_cfg/templates/admin_old/components/status_badge.html +0 -28
  134. django_cfg/templates/admin_old/components/user_avatar.html +0 -27
  135. django_cfg/templates/admin_old/constance/change_list.html +0 -74
  136. django_cfg/templates/admin_old/constance/includes/default_value.html +0 -24
  137. django_cfg/templates/admin_old/constance/includes/fieldset_header.html +0 -15
  138. django_cfg/templates/admin_old/constance/includes/results_list.html +0 -16
  139. django_cfg/templates/admin_old/constance/includes/setting_row.html +0 -50
  140. django_cfg/templates/admin_old/constance/includes/table_headers.html +0 -10
  141. django_cfg/templates/admin_old/examples/component_class_example.html +0 -156
  142. django_cfg/templates/admin_old/import_export/change_list_export.html +0 -24
  143. django_cfg/templates/admin_old/import_export/change_list_import.html +0 -24
  144. django_cfg/templates/admin_old/import_export/change_list_import_export.html +0 -34
  145. django_cfg/templates/admin_old/index.html +0 -80
  146. django_cfg/templates/admin_old/index_new.html +0 -119
  147. django_cfg/templates/admin_old/layouts/base_dashboard.html +0 -62
  148. django_cfg/templates/admin_old/layouts/dashboard_with_tabs.html +0 -176
  149. django_cfg/templates/admin_old/sections/commands_section.html +0 -549
  150. django_cfg/templates/admin_old/sections/documentation_section.html +0 -152
  151. django_cfg/templates/admin_old/sections/overview_section.html +0 -112
  152. django_cfg/templates/admin_old/sections/stats_section.html +0 -35
  153. django_cfg/templates/admin_old/sections/system_section.html +0 -99
  154. django_cfg/templates/admin_old/sections/widgets_section.html +0 -129
  155. django_cfg/templates/admin_old/snippets/components/activity_tracker.html +0 -70
  156. django_cfg/templates/admin_old/snippets/components/charts_section.html +0 -113
  157. django_cfg/templates/admin_old/snippets/components/django_commands.html +0 -270
  158. django_cfg/templates/admin_old/snippets/components/quick_actions.html +0 -66
  159. django_cfg/templates/admin_old/snippets/components/recent_activity_improved.html +0 -25
  160. django_cfg/templates/admin_old/snippets/components/recent_users_table.html +0 -102
  161. django_cfg/templates/admin_old/snippets/components/stats_cards.html +0 -4
  162. django_cfg/templates/admin_old/snippets/components/stats_tiles.html +0 -92
  163. django_cfg/templates/admin_old/snippets/components/system_health.html +0 -22
  164. django_cfg/templates/admin_old/snippets/components/system_metrics.html +0 -199
  165. django_cfg/templates/admin_old/snippets/components/user_permissions.html +0 -57
  166. django_cfg/templates/admin_old/snippets/tabs/app_stats_tab.html +0 -201
  167. django_cfg/templates/admin_old/snippets/tabs/commands_tab.html +0 -114
  168. django_cfg/templates/admin_old/snippets/tabs/documentation_tab.html +0 -42
  169. django_cfg/templates/admin_old/snippets/tabs/overview_tab.html +0 -116
  170. django_cfg/templates/admin_old/snippets/tabs/stats_tab.html +0 -89
  171. django_cfg/templates/admin_old/snippets/tabs/users_tab.html +0 -51
  172. django_cfg/templates/admin_old/snippets/tabs/widgets_tab.html +0 -38
  173. django_cfg/templates/admin_old/snippets/zones/zones_table.html +0 -176
  174. /django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_ssgManifest.js +0 -0
  175. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/WHEEL +0 -0
  176. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/entry_points.txt +0 -0
  177. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/licenses/LICENSE +0 -0
@@ -1,222 +0,0 @@
1
- """Widgets section for dashboard.
2
-
3
- Automatically renders widgets from DashboardManager.get_widgets_config()
4
- """
5
-
6
- from datetime import timedelta
7
- from typing import Any, Dict, List
8
-
9
- import psutil
10
- from django.db.models import Avg
11
- from django.template import Context, Template
12
- from django.utils import timezone
13
-
14
- from .base import DataSection
15
-
16
-
17
- class WidgetsSection(DataSection):
18
- """
19
- Widgets section showing automatically generated dashboard widgets.
20
-
21
- Widgets are defined in DashboardManager.get_widgets_config() and
22
- can include:
23
- - System metrics (CPU, Memory, Disk)
24
- - RPC monitoring stats
25
- - Custom application widgets
26
- """
27
-
28
- template_name = "admin/sections/widgets_section.html"
29
- title = "Dashboard Widgets"
30
- icon = "widgets"
31
-
32
- def get_data(self) -> Dict[str, Any]:
33
- """Get widgets configuration from DashboardManager."""
34
- from django_cfg.modules.django_unfold.dashboard import get_dashboard_manager
35
-
36
- dashboard_manager = get_dashboard_manager()
37
-
38
- # Get widgets from dashboard manager (base system widgets)
39
- widgets = dashboard_manager.get_widgets_config()
40
-
41
- return {
42
- 'widgets': widgets,
43
- 'widgets_count': len(widgets),
44
- 'has_widgets': len(widgets) > 0,
45
- }
46
-
47
- def merge_custom_widgets(self, widgets: List[Dict], custom_widgets: List[Any]) -> List[Dict]:
48
- """
49
- Merge custom widgets from dashboard_callback.
50
-
51
- Allows projects to add widgets via dashboard_callback:
52
- context["custom_widgets"] = [
53
- StatsCardsWidget(...),
54
- ...
55
- ]
56
- """
57
- if not custom_widgets:
58
- return widgets
59
-
60
- # Convert custom widgets to dicts if they are Pydantic models
61
- for widget in custom_widgets:
62
- if hasattr(widget, 'to_dict'):
63
- widgets.append(widget.to_dict())
64
- elif hasattr(widget, 'model_dump'):
65
- widgets.append(widget.model_dump())
66
- elif isinstance(widget, dict):
67
- widgets.append(widget)
68
-
69
- return widgets
70
-
71
- def get_context_data(self, **kwargs) -> Dict[str, Any]:
72
- """Add additional context for widget rendering."""
73
- context = super().get_context_data(**kwargs)
74
-
75
- # Get base widgets from DashboardManager
76
- widgets = context['data']['widgets']
77
-
78
- # Merge custom widgets from dashboard_callback if provided
79
- custom_widgets_from_callback = kwargs.get('custom_widgets', [])
80
- if custom_widgets_from_callback:
81
- widgets = self.merge_custom_widgets(widgets, custom_widgets_from_callback)
82
- # Update count
83
- context['data']['widgets_count'] = len(widgets)
84
- context['data']['has_widgets'] = len(widgets) > 0
85
-
86
- # Get metrics data first
87
- metrics_data = {}
88
- metrics_data.update(self.get_system_metrics())
89
-
90
- # Add Centrifugo metrics if enabled
91
- dashboard_manager = self._get_dashboard_manager()
92
- if dashboard_manager.is_centrifugo_enabled():
93
- metrics_data.update(self.get_centrifugo_metrics())
94
-
95
- # Also merge any custom metrics from kwargs
96
- custom_metrics = kwargs.get('custom_metrics', {})
97
- if custom_metrics:
98
- metrics_data.update(custom_metrics)
99
-
100
- # Process widgets and resolve template variables
101
- processed_stats_widgets = []
102
- for widget in widgets:
103
- if widget.get('type') == 'stats_cards':
104
- processed_widget = self._process_stats_widget(widget, metrics_data)
105
- processed_stats_widgets.append(processed_widget)
106
-
107
- chart_widgets = [w for w in widgets if w.get('type') == 'chart']
108
- custom_widgets = [w for w in widgets if w.get('type') not in ['stats_cards', 'chart']]
109
-
110
- # Add processed widgets
111
- context.update({
112
- 'stats_widgets': processed_stats_widgets,
113
- 'chart_widgets': chart_widgets,
114
- 'custom_widgets': custom_widgets,
115
- })
116
-
117
- # Also add metrics for direct access
118
- context.update(metrics_data)
119
-
120
- return context
121
-
122
- def _get_dashboard_manager(self):
123
- """Get dashboard manager instance (lazy import to avoid circular dependencies)."""
124
- from django_cfg.modules.django_unfold.dashboard import get_dashboard_manager
125
- return get_dashboard_manager()
126
-
127
- def _process_stats_widget(self, widget: Dict[str, Any], context_data: Dict[str, Any]) -> Dict[str, Any]:
128
- """Process StatsCardsWidget and resolve template variables in cards."""
129
- processed_widget = widget.copy()
130
- processed_cards = []
131
-
132
- for card in widget.get('cards', []):
133
- processed_card = card.copy()
134
-
135
- # Resolve value_template using Django template engine
136
- value_template = card.get('value_template', '')
137
- if '{{' in value_template:
138
- try:
139
- template = Template(value_template)
140
- context = Context(context_data)
141
- resolved_value = template.render(context)
142
- processed_card['value_template'] = resolved_value
143
- except Exception:
144
- # Keep original if rendering fails
145
- pass
146
-
147
- # Also resolve change field if it has template variables
148
- change_template = card.get('change', '')
149
- if change_template and '{{' in change_template:
150
- try:
151
- template = Template(change_template)
152
- context = Context(context_data)
153
- resolved_change = template.render(context)
154
- processed_card['change'] = resolved_change
155
- except Exception:
156
- # Keep original if rendering fails
157
- pass
158
-
159
- processed_cards.append(processed_card)
160
-
161
- processed_widget['cards'] = processed_cards
162
- return processed_widget
163
-
164
- def get_system_metrics(self) -> Dict[str, Any]:
165
- """Get system metrics for widgets."""
166
- return {
167
- 'cpu_percent': round(psutil.cpu_percent(interval=0.1), 1),
168
- 'memory_percent': round(psutil.virtual_memory().percent, 1),
169
- 'disk_percent': round(psutil.disk_usage('/').percent, 1),
170
- }
171
-
172
- def get_centrifugo_metrics(self) -> Dict[str, Any]:
173
- """Get Centrifugo metrics for widgets."""
174
- try:
175
- from django_cfg.apps.centrifugo.models import CentrifugoLog
176
-
177
- # Get stats for last 24 hours
178
- since = timezone.now() - timedelta(hours=24)
179
-
180
- logs = CentrifugoLog.objects.filter(created_at__gte=since)
181
-
182
- total_publishes = logs.count()
183
- successful_publishes = logs.filter(status='success').count()
184
- failed_publishes = logs.filter(status='failed').count()
185
- timeout_publishes = logs.filter(status='timeout').count()
186
-
187
- success_rate = round((successful_publishes / total_publishes * 100) if total_publishes > 0 else 0, 1)
188
-
189
- avg_duration = logs.filter(
190
- duration_ms__isnull=False
191
- ).aggregate(
192
- avg=Avg('duration_ms')
193
- )['avg']
194
-
195
- avg_duration = round(avg_duration, 1) if avg_duration else 0 # Already in ms
196
-
197
- avg_acks = logs.filter(
198
- acks_received__isnull=False
199
- ).aggregate(
200
- avg=Avg('acks_received')
201
- )['avg']
202
-
203
- avg_acks = round(avg_acks, 1) if avg_acks else 0
204
-
205
- return {
206
- 'centrifugo_total_publishes': total_publishes,
207
- 'centrifugo_success_rate': success_rate,
208
- 'centrifugo_avg_duration': avg_duration,
209
- 'centrifugo_failed_publishes': failed_publishes,
210
- 'centrifugo_timeout_publishes': timeout_publishes,
211
- 'centrifugo_avg_acks': avg_acks,
212
- }
213
- except Exception as e:
214
- # Return zeros if Centrifugo models not available
215
- return {
216
- 'centrifugo_total_publishes': 0,
217
- 'centrifugo_success_rate': 0,
218
- 'centrifugo_avg_duration': 0,
219
- 'centrifugo_failed_publishes': 0,
220
- 'centrifugo_timeout_publishes': 0,
221
- 'centrifugo_avg_acks': 0,
222
- }
@@ -1,9 +0,0 @@
1
- """
2
- Django CFG Unfold Callbacks Module
3
-
4
- Modular callback system for Django Unfold dashboard.
5
- """
6
-
7
- from .main import UnfoldCallbacks
8
-
9
- __all__ = ['UnfoldCallbacks']
@@ -1,51 +0,0 @@
1
- """
2
- Quick actions callbacks.
3
- """
4
-
5
- import logging
6
- from typing import List
7
-
8
- from django_cfg.modules.django_admin.icons import Icons
9
-
10
- from ..models.dashboard import QuickAction
11
- from .base import get_user_admin_urls
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- class ActionsCallbacks:
17
- """Quick actions callbacks."""
18
-
19
- def get_quick_actions(self) -> List[QuickAction]:
20
- """Get quick action buttons as Pydantic models."""
21
- # Get user admin URLs dynamically based on AUTH_USER_MODEL
22
- user_admin_urls = get_user_admin_urls()
23
-
24
- actions = [
25
- QuickAction(
26
- title="Add User",
27
- description="Create new user account",
28
- icon=Icons.PERSON_ADD,
29
- link=user_admin_urls["add"],
30
- color="primary",
31
- category="admin",
32
- ),
33
- QuickAction(
34
- title="Support Tickets",
35
- description="Manage support tickets",
36
- icon=Icons.SUPPORT_AGENT,
37
- link="admin:django_cfg_support_ticket_changelist",
38
- color="primary",
39
- category="support",
40
- ),
41
- QuickAction(
42
- title="Health Check",
43
- description="System health status",
44
- icon=Icons.HEALTH_AND_SAFETY,
45
- link="/cfg/health/",
46
- color="success",
47
- category="system",
48
- ),
49
- ]
50
-
51
- return actions
@@ -1,122 +0,0 @@
1
- """
2
- Django Client (OpenAPI) integration callbacks.
3
- """
4
-
5
- import logging
6
- from typing import Any, Dict, List, Tuple
7
-
8
- from django.conf import settings
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- class OpenAPIClientCallbacks:
14
- """Django Client (OpenAPI) integration callbacks."""
15
-
16
- def get_openapi_groups_data(self) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
17
- """Get Django Client (OpenAPI) groups data."""
18
- try:
19
- # Get groups from OpenAPI service (includes default cfg group)
20
- from django_cfg.modules.django_client.core import get_openapi_service
21
-
22
- service = get_openapi_service()
23
- if not service.config:
24
- return [], {"total_apps": 0, "total_endpoints": 0, "total_groups": 0}
25
-
26
- # Get groups with defaults (includes cfg group automatically)
27
- groups_dict = service.get_groups()
28
- groups_list = list(groups_dict.values())
29
- api_prefix = getattr(service.config, "api_prefix", "api")
30
-
31
- # Ensure urlconf modules AND URL patterns are created for all groups with apps
32
- try:
33
- from django.urls import path
34
- from drf_spectacular.views import SpectacularAPIView
35
- from django_cfg.modules.django_client.core.groups import GroupManager
36
- from django_cfg.modules.django_client import urls as client_urls
37
-
38
- manager = GroupManager(service.config)
39
- for group in groups_list:
40
- group_name = getattr(group, "name", "unknown") if not isinstance(group, dict) else group.get("name", "unknown")
41
- apps = getattr(group, "apps", []) if not isinstance(group, dict) else group.get("apps", [])
42
- group_version = getattr(group, "version", "1.0.0") if not isinstance(group, dict) else group.get("version", "1.0.0")
43
-
44
- if apps:
45
- try:
46
- # Create urlconf module
47
- manager.create_urlconf_module(group_name)
48
-
49
- # Check if URL pattern already exists
50
- url_name = f'openapi-schema-{group_name}'
51
- url_exists = any(
52
- hasattr(pattern, 'name') and pattern.name == url_name
53
- for pattern in client_urls.urlpatterns
54
- )
55
-
56
- # Add URL pattern if it doesn't exist
57
- if not url_exists:
58
- new_pattern = path(
59
- f'{group_name}/schema/',
60
- SpectacularAPIView.as_view(
61
- urlconf=f'_django_client_urlconf_{group_name}',
62
- api_version=group_version,
63
- ),
64
- name=url_name,
65
- )
66
- client_urls.urlpatterns.append(new_pattern)
67
- except Exception:
68
- pass # Silently skip if already exists or fails
69
- except Exception:
70
- pass # Silently skip if GroupManager fails
71
-
72
- groups_data = []
73
- total_apps = 0
74
- total_endpoints = 0
75
-
76
- for group in groups_list:
77
- # Handle both dict and object access
78
- if isinstance(group, dict):
79
- group_name = group.get("name", "unknown")
80
- title = group.get("title", group_name.title())
81
- description = group.get("description", f"{group_name} group")
82
- apps = group.get("apps", [])
83
- else:
84
- # Handle object access (for OpenAPIGroupConfig instances)
85
- group_name = getattr(group, "name", "unknown")
86
- title = getattr(group, "title", group_name.title())
87
- description = getattr(group, "description", f"{group_name} group")
88
- apps = getattr(group, "apps", [])
89
-
90
- # Count actual endpoints by checking URL patterns (simplified estimate)
91
- endpoint_count = len(apps) * 3 # Conservative estimate
92
-
93
- groups_data.append({
94
- "name": group_name,
95
- "title": title,
96
- "description": description,
97
- "app_count": len(apps),
98
- "endpoint_count": endpoint_count,
99
- "status": "active",
100
- "schema_url": f"/cfg/openapi/{group_name}/schema/",
101
- "api_url": f"/{api_prefix}/{group_name}/",
102
- })
103
-
104
- total_apps += len(apps)
105
- total_endpoints += endpoint_count
106
-
107
- return groups_data, {
108
- "total_apps": total_apps,
109
- "total_endpoints": total_endpoints,
110
- "total_groups": len(groups_list),
111
- }
112
- except Exception as e:
113
- logger.error(f"Error getting OpenAPI groups: {e}")
114
- return [], {
115
- "total_apps": 0,
116
- "total_endpoints": 0,
117
- "total_groups": 0,
118
- }
119
-
120
-
121
- # Keep backward compatibility alias
122
- RevolutionCallbacks = OpenAPIClientCallbacks
@@ -1,223 +0,0 @@
1
- """
2
- Charts data callbacks.
3
- """
4
-
5
- import logging
6
- from datetime import timedelta
7
- from typing import Any, Dict, List
8
-
9
- from django.contrib.auth import get_user_model
10
- from django.db.models import Count
11
- from django.utils import timezone
12
-
13
- from ..models.dashboard import ChartData, ChartDataset
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class ChartsCallbacks:
19
- """Charts data callbacks."""
20
-
21
- def _get_user_model(self):
22
- """Get the user model safely."""
23
- return get_user_model()
24
-
25
- def _get_empty_chart_data(self, label: str) -> Dict[str, Any]:
26
- """Get empty chart data structure."""
27
- return ChartData(
28
- labels=["No Data"],
29
- datasets=[
30
- ChartDataset(
31
- label=label,
32
- data=[0],
33
- backgroundColor="rgba(156, 163, 175, 0.1)",
34
- borderColor="rgb(156, 163, 175)",
35
- tension=0.4
36
- )
37
- ]
38
- ).model_dump()
39
-
40
- def get_user_registration_chart_data(self) -> Dict[str, Any]:
41
- """Get user registration chart data."""
42
- try:
43
- # Avoid database access during app initialization
44
- from django.apps import apps
45
- if not apps.ready:
46
- return self._get_empty_chart_data("New Users")
47
-
48
- User = self._get_user_model()
49
-
50
- # Get last 7 days of registration data
51
- end_date = timezone.now().date()
52
- start_date = end_date - timedelta(days=6)
53
-
54
- # Generate date range
55
- date_range = []
56
- current_date = start_date
57
- while current_date <= end_date:
58
- date_range.append(current_date)
59
- current_date += timedelta(days=1)
60
-
61
- # Get registration counts by date
62
- registration_data = (
63
- User.objects.filter(date_joined__date__gte=start_date)
64
- .extra({'date': "date(date_joined)"})
65
- .values('date')
66
- .annotate(count=Count('id'))
67
- .order_by('date')
68
- )
69
-
70
- # Create data dictionary for easy lookup
71
- data_dict = {item['date']: item['count'] for item in registration_data}
72
-
73
- # Build chart data
74
- labels = [date.strftime("%m/%d") for date in date_range]
75
- data_points = [data_dict.get(date, 0) for date in date_range]
76
-
77
- chart_data = ChartData(
78
- labels=labels,
79
- datasets=[
80
- ChartDataset(
81
- label="New Users",
82
- data=data_points,
83
- backgroundColor="rgba(59, 130, 246, 0.1)",
84
- borderColor="rgb(59, 130, 246)",
85
- tension=0.4
86
- )
87
- ]
88
- )
89
-
90
- return chart_data.model_dump()
91
-
92
- except Exception as e:
93
- logger.error(f"Error getting user registration chart data: {e}")
94
- return self._get_empty_chart_data("New Users")
95
-
96
- def get_user_activity_chart_data(self) -> Dict[str, Any]:
97
- """Get user activity chart data."""
98
- try:
99
- # Avoid database access during app initialization
100
- from django.apps import apps
101
- if not apps.ready:
102
- return self._get_empty_chart_data("Active Users")
103
-
104
- User = self._get_user_model()
105
-
106
- # Get activity data for last 7 days
107
- end_date = timezone.now().date()
108
- start_date = end_date - timedelta(days=6)
109
-
110
- # Generate date range
111
- date_range = []
112
- current_date = start_date
113
- while current_date <= end_date:
114
- date_range.append(current_date)
115
- current_date += timedelta(days=1)
116
-
117
- # Get login activity (users who logged in each day)
118
- activity_data = (
119
- User.objects.filter(last_login__date__gte=start_date, last_login__isnull=False)
120
- .extra({'date': "date(last_login)"})
121
- .values('date')
122
- .annotate(count=Count('id'))
123
- .order_by('date')
124
- )
125
-
126
- # Create data dictionary for easy lookup
127
- data_dict = {item['date']: item['count'] for item in activity_data}
128
-
129
- # Build chart data
130
- labels = [date.strftime("%m/%d") for date in date_range]
131
- data_points = [data_dict.get(date, 0) for date in date_range]
132
-
133
- chart_data = ChartData(
134
- labels=labels,
135
- datasets=[
136
- ChartDataset(
137
- label="Active Users",
138
- data=data_points,
139
- backgroundColor="rgba(34, 197, 94, 0.1)",
140
- borderColor="rgb(34, 197, 94)",
141
- tension=0.4
142
- )
143
- ]
144
- )
145
-
146
- return chart_data.model_dump()
147
-
148
- except Exception as e:
149
- logger.error(f"Error getting user activity chart data: {e}")
150
- return self._get_empty_chart_data("Active Users")
151
-
152
- def get_activity_tracker_data(self) -> List[Dict[str, str]]:
153
- """Get activity tracker data for the last 52 weeks (GitHub-style)."""
154
- try:
155
- # Avoid database access during app initialization
156
- from django.apps import apps
157
- if not apps.ready:
158
- return self._get_empty_tracker_data()
159
-
160
- User = self._get_user_model()
161
-
162
- # Get data for last 52 weeks (365 days)
163
- end_date = timezone.now().date()
164
- start_date = end_date - timedelta(days=364) # 52 weeks * 7 days - 1
165
-
166
- # Get activity data by date
167
- activity_data = (
168
- User.objects.filter(last_login__date__gte=start_date, last_login__isnull=False)
169
- .extra({'date': "date(last_login)"})
170
- .values('date')
171
- .annotate(count=Count('id'))
172
- .order_by('date')
173
- )
174
-
175
- # Create data dictionary for easy lookup
176
- data_dict = {item['date']: item['count'] for item in activity_data}
177
-
178
- # Generate tracker data for each day
179
- tracker_data = []
180
- current_date = start_date
181
-
182
- while current_date <= end_date:
183
- activity_count = data_dict.get(current_date, 0)
184
-
185
- # Determine color based on activity level
186
- if activity_count == 0:
187
- color = "bg-base-200 dark:bg-base-700"
188
- level = "No activity"
189
- elif activity_count <= 2:
190
- color = "bg-green-200 dark:bg-green-800"
191
- level = "Low activity"
192
- elif activity_count <= 5:
193
- color = "bg-green-400 dark:bg-green-600"
194
- level = "Medium activity"
195
- elif activity_count <= 10:
196
- color = "bg-green-600 dark:bg-green-500"
197
- level = "High activity"
198
- else:
199
- color = "bg-green-800 dark:bg-green-400"
200
- level = "Very high activity"
201
-
202
- tracker_data.append({
203
- "color": color,
204
- "tooltip": f"{current_date.strftime('%Y-%m-%d')}: {activity_count} active users ({level})"
205
- })
206
-
207
- current_date += timedelta(days=1)
208
-
209
- return tracker_data
210
-
211
- except Exception as e:
212
- logger.error(f"Error getting activity tracker data: {e}")
213
- return self._get_empty_tracker_data()
214
-
215
- def _get_empty_tracker_data(self) -> List[Dict[str, str]]:
216
- """Get empty tracker data (365 days of no activity)."""
217
- tracker_data = []
218
- for i in range(365):
219
- tracker_data.append({
220
- "color": "bg-base-200 dark:bg-base-700",
221
- "tooltip": f"Day {i + 1}: No data available"
222
- })
223
- return tracker_data
@@ -1,40 +0,0 @@
1
- """
2
- Django commands callbacks.
3
- """
4
-
5
- import logging
6
- from typing import Any, Dict
7
-
8
- from .base import get_available_commands, get_commands_by_category
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- class CommandsCallbacks:
14
- """Django commands callbacks."""
15
-
16
- def get_django_commands(self) -> Dict[str, Any]:
17
- """Get Django management commands information."""
18
- try:
19
- commands = get_available_commands()
20
- categorized = get_commands_by_category()
21
-
22
- return {
23
- "commands": commands,
24
- "categorized": categorized,
25
- "total_commands": len(commands),
26
- "categories": list(categorized.keys()),
27
- "core_commands": len([cmd for cmd in commands if cmd['is_core']]),
28
- "custom_commands": len([cmd for cmd in commands if cmd['is_custom']]),
29
- }
30
- except Exception as e:
31
- logger.error(f"Error getting Django commands: {e}")
32
- # Return safe fallback to prevent dashboard from breaking
33
- return {
34
- "commands": [],
35
- "categorized": {},
36
- "total_commands": 0,
37
- "categories": [],
38
- "core_commands": 0,
39
- "custom_commands": 0,
40
- }