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,322 +0,0 @@
1
- """
2
- Main Unfold Dashboard Callbacks
3
-
4
- Combines all callback modules into a single interface.
5
- """
6
-
7
- import json
8
- import logging
9
- from typing import Any, Dict
10
-
11
- from django.template.loader import render_to_string
12
- from django.utils import timezone
13
-
14
- from django_cfg.core.state import get_current_config
15
- from django_cfg.modules.django_dashboard.debug import save_dashboard_render
16
- from django_cfg.modules.django_dashboard.sections.commands import CommandsSection
17
- from django_cfg.modules.django_dashboard.sections.documentation import DocumentationSection
18
-
19
- # Import new dashboard sections
20
- from django_cfg.modules.django_dashboard.sections.overview import OverviewSection
21
- from django_cfg.modules.django_dashboard.sections.stats import StatsSection
22
- from django_cfg.modules.django_dashboard.sections.system import SystemSection
23
- from django_cfg.modules.django_dashboard.sections.widgets import WidgetsSection
24
-
25
- from ...base import BaseCfgModule
26
- from ..models.dashboard import DashboardData
27
- from .actions import ActionsCallbacks
28
- from .base import get_user_admin_urls
29
- from .charts import ChartsCallbacks
30
- from .commands import CommandsCallbacks
31
- from .apizones import OpenAPIClientCallbacks
32
- from .statistics import StatisticsCallbacks
33
- from .system import SystemCallbacks
34
- from .users import UsersCallbacks
35
-
36
- logger = logging.getLogger(__name__)
37
-
38
-
39
- class UnfoldCallbacks(
40
- BaseCfgModule,
41
- StatisticsCallbacks,
42
- SystemCallbacks,
43
- ActionsCallbacks,
44
- ChartsCallbacks,
45
- CommandsCallbacks,
46
- OpenAPIClientCallbacks,
47
- UsersCallbacks
48
- ):
49
- """
50
- Main Unfold dashboard callbacks with full system monitoring.
51
-
52
- Combines all callback modules using multiple inheritance for
53
- clean separation of concerns while maintaining a single interface.
54
- """
55
-
56
- def main_dashboard_callback(self, request, context: Dict[str, Any]) -> Dict[str, Any]:
57
- """
58
- Main dashboard callback function with comprehensive system data.
59
-
60
- Returns all dashboard data as Pydantic models for type safety.
61
- """
62
- try:
63
- # Get current config for debug and environment settings
64
- config = get_current_config()
65
-
66
- # Log debug status
67
- debug_enabled = config and config.debug
68
- logger.info(f"[DEBUG MODE] Dashboard rendering with debug={debug_enabled} (config.debug={getattr(config, 'debug', 'N/A')})")
69
-
70
- # Get dashboard data first
71
- user_stats = self.get_user_statistics()
72
- support_stats = self.get_support_statistics()
73
- system_health = self.get_system_health()
74
- quick_actions = self.get_quick_actions()
75
-
76
- # Parse time range from query parameters (default: 7 days)
77
- try:
78
- time_range = int(request.GET.get('range', 7))
79
- # Validate range
80
- if time_range not in [7, 30, 90]:
81
- time_range = 7
82
- except (ValueError, TypeError):
83
- time_range = 7
84
-
85
- # Create navigation for time range filter
86
- navigation = [
87
- {
88
- "title": "7 Days",
89
- "link": "?range=7",
90
- "active": time_range == 7,
91
- "icon": "calendar_today"
92
- },
93
- {
94
- "title": "30 Days",
95
- "link": "?range=30",
96
- "active": time_range == 30,
97
- "icon": "date_range"
98
- },
99
- {
100
- "title": "90 Days",
101
- "link": "?range=90",
102
- "active": time_range == 90,
103
- "icon": "event"
104
- },
105
- ]
106
-
107
- # Render new dashboard sections
108
- try:
109
- # Create overview section and pass quick_actions + time_range + navigation
110
- overview_sec = OverviewSection(request)
111
- # Add quick_actions, time_range, and navigation to render context
112
- overview_section = overview_sec.render(
113
- quick_actions=[action.model_dump() for action in quick_actions],
114
- time_range=time_range,
115
- navigation=navigation
116
- )
117
- except Exception as e:
118
- logger.error(f"Failed to render overview section: {e}", exc_info=True)
119
- overview_section = None
120
-
121
- try:
122
- stats_section = StatsSection(request).render()
123
- except Exception as e:
124
- logger.error(f"Failed to render stats section: {e}", exc_info=True)
125
- stats_section = None
126
-
127
- try:
128
- system_section = SystemSection(request).render()
129
- except Exception as e:
130
- logger.error(f"Failed to render system section: {e}", exc_info=True)
131
- system_section = None
132
-
133
- try:
134
- # Generate documentation content first
135
- doc_section = DocumentationSection(request)
136
- doc_data = doc_section.get_data_old() # Get markdown/HTML content
137
- documentation_content = doc_data.get('documentation_content') or doc_data.get('readme_content', '')
138
- documentation_section = doc_section.render()
139
- except Exception as e:
140
- logger.error(f"Failed to render documentation section: {e}", exc_info=True)
141
- documentation_section = None
142
- documentation_content = None
143
-
144
- try:
145
- # Render commands section with documentation content
146
- commands_section = CommandsSection(request).render(
147
- documentation_content=documentation_content
148
- )
149
- except Exception as e:
150
- logger.error(f"Failed to render commands section: {e}", exc_info=True)
151
- commands_section = None
152
-
153
- # Extract custom widgets from context if provided by project's dashboard_callback
154
- custom_widgets = context.get('custom_widgets', [])
155
- custom_metrics = {}
156
-
157
- # Extract all metric-like variables from context for widget template resolution
158
- # This allows dashboard_callback to add metrics like: context['total_users'] = 123
159
- for key, value in context.items():
160
- if key not in ['request', 'cards', 'system_health', 'quick_actions'] and isinstance(value, (int, float, str)):
161
- custom_metrics[key] = value
162
-
163
- try:
164
- # Render widgets section with custom widgets and metrics from callback
165
- widgets_section = WidgetsSection(request).render(
166
- custom_widgets=custom_widgets,
167
- custom_metrics=custom_metrics
168
- )
169
- except Exception as e:
170
- logger.error(f"Failed to render widgets section: {e}", exc_info=True)
171
- widgets_section = None
172
-
173
- # Combine all stat cards (data already loaded above)
174
- all_stats = user_stats + support_stats
175
-
176
- dashboard_data = DashboardData(
177
- stat_cards=all_stats,
178
- system_health=system_health,
179
- quick_actions=quick_actions,
180
- last_updated=timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
181
- environment=getattr(config.environment, 'name', 'development') if config and hasattr(config, 'environment') else "development",
182
- )
183
-
184
- # Convert to template context (using to_dict for Unfold compatibility)
185
- cards_data = [card.to_dict() for card in dashboard_data.stat_cards]
186
-
187
- context.update({
188
- # New dashboard sections (rendered HTML)
189
- "overview_section": overview_section,
190
- "stats_section": stats_section,
191
- "system_section": system_section,
192
- "commands_section": commands_section,
193
- "documentation_section": documentation_section,
194
- "widgets_section": widgets_section,
195
-
196
- # Documentation content for commands tab
197
- "documentation_content": documentation_content,
198
-
199
- # Statistics cards
200
- "cards": cards_data,
201
- "user_stats": [card.to_dict() for card in user_stats],
202
- "support_stats": [card.to_dict() for card in support_stats],
203
-
204
- # System health (convert to dict for template)
205
- "system_health": {
206
- item.component + "_status": item.status
207
- for item in dashboard_data.system_health
208
- },
209
-
210
- # Quick actions
211
- "quick_actions": [
212
- action.model_dump() for action in dashboard_data.quick_actions
213
- ],
214
-
215
- # Additional categorized actions
216
- "admin_actions": [
217
- action.model_dump()
218
- for action in dashboard_data.quick_actions
219
- if action.category == "admin"
220
- ],
221
- "support_actions": [
222
- action.model_dump()
223
- for action in dashboard_data.quick_actions
224
- if action.category == "support"
225
- ],
226
- "system_actions": [
227
- action.model_dump()
228
- for action in dashboard_data.quick_actions
229
- if action.category == "system"
230
- ],
231
-
232
- # OpenAPI Client groups
233
- "zones_table": {
234
- "headers": [
235
- {"label": "Zone"},
236
- {"label": "Title"},
237
- {"label": "Apps"},
238
- {"label": "Endpoints"},
239
- {"label": "Status"},
240
- {"label": "Actions"},
241
- ],
242
- "rows": OpenAPIClientCallbacks().get_openapi_groups_data()[0],
243
- },
244
-
245
- # Recent users
246
- "recent_users": self.get_recent_users(),
247
- "user_admin_urls": get_user_admin_urls(),
248
-
249
- # App statistics
250
- "app_statistics": self.get_app_statistics(),
251
-
252
- # Django commands
253
- "django_commands": self.get_django_commands(),
254
-
255
- # Charts data - serialize to JSON for JavaScript
256
- "charts": {
257
- "user_registrations_json": json.dumps(self.get_user_registration_chart_data()),
258
- "user_activity_json": json.dumps(self.get_user_activity_chart_data()),
259
- "user_registrations": self.get_user_registration_chart_data(),
260
- "user_activity": self.get_user_activity_chart_data(),
261
- },
262
-
263
- # Activity tracker data
264
- "activity_tracker": self.get_activity_tracker_data(),
265
-
266
-
267
- # Meta information
268
- "last_updated": dashboard_data.last_updated,
269
- "environment": dashboard_data.environment,
270
- "dashboard_title": "Django CFG Dashboard",
271
- })
272
-
273
- # Log charts data for debugging
274
- # charts_data = context.get('charts', {})
275
- # # logger.info(f"Charts data added to context: {list(charts_data.keys())}")
276
- # if 'user_registrations' in charts_data:
277
- # reg_data = charts_data['user_registrations']
278
- # logger.info(f"Registration chart labels: {reg_data.get('labels', [])}")
279
- # if 'user_activity' in charts_data:
280
- # act_data = charts_data['user_activity']
281
- # logger.info(f"Activity chart labels: {act_data.get('labels', [])}")
282
-
283
- # # Log recent users data for debugging
284
- # recent_users_data = context.get('recent_users', [])
285
- # logger.info(f"Recent users data count: {len(recent_users_data)}")
286
- # if recent_users_data:
287
- # logger.info(f"First user: {recent_users_data[0].get('username', 'N/A')}")
288
-
289
- # # Log activity tracker data for debugging
290
- # activity_tracker_data = context.get('activity_tracker', [])
291
- # logger.info(f"Activity tracker data count: {len(activity_tracker_data)}")
292
-
293
- # Debug: save full rendered page (only in debug mode)
294
- # DISABLED: Commenting out dashboard render saving to disk
295
- # if config and config.debug:
296
- # try:
297
- # full_html = render_to_string('admin/index.html', context, request)
298
- # save_dashboard_render(full_html, name='dashboard_full_page', context=context)
299
- # except Exception as e:
300
- # logger.error(f"Failed to save full dashboard render: {e}", exc_info=True)
301
-
302
- return context
303
-
304
- except Exception as e:
305
- logger.error(f"Dashboard callback error: {e}")
306
- # Return minimal safe defaults
307
- context.update({
308
- "cards": [
309
- {
310
- "title": "System Error",
311
- "value": "N/A",
312
- "icon": "error",
313
- "color": "danger",
314
- "description": "Dashboard data unavailable"
315
- }
316
- ],
317
- "system_health": {},
318
- "quick_actions": [],
319
- "last_updated": timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
320
- "error": f"Dashboard error: {str(e)}",
321
- })
322
- return context
@@ -1,240 +0,0 @@
1
- """
2
- Statistics callbacks for dashboard.
3
- """
4
-
5
- import logging
6
- from datetime import timedelta
7
- from typing import Any, Dict, List
8
-
9
- from django.apps import apps
10
- from django.contrib.auth import get_user_model
11
- from django.utils import timezone
12
-
13
- from django_cfg.modules.django_admin.icons import Icons
14
-
15
- from ..models.dashboard import StatCard
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- class StatisticsCallbacks:
21
- """Statistics-related callbacks."""
22
-
23
- def _get_user_model(self):
24
- """Get the user model safely."""
25
- return get_user_model()
26
-
27
- def get_user_statistics(self) -> List[StatCard]:
28
- """Get user-related statistics as Pydantic models."""
29
- try:
30
- User = self._get_user_model()
31
-
32
- total_users = User.objects.count()
33
- active_users = User.objects.filter(is_active=True).count()
34
- new_users_7d = User.objects.filter(
35
- date_joined__gte=timezone.now() - timedelta(days=7)
36
- ).count()
37
- staff_users = User.objects.filter(is_staff=True).count()
38
-
39
- return [
40
- StatCard(
41
- title="Total Users",
42
- value=f"{total_users:,}",
43
- icon=Icons.PEOPLE,
44
- change=f"+{new_users_7d}" if new_users_7d > 0 else None,
45
- change_type="positive" if new_users_7d > 0 else "neutral",
46
- description="Registered users",
47
- ),
48
- StatCard(
49
- title="Active Users",
50
- value=f"{active_users:,}",
51
- icon=Icons.PERSON,
52
- change=(
53
- f"{(active_users/total_users*100):.1f}%"
54
- if total_users > 0
55
- else "0%"
56
- ),
57
- change_type=(
58
- "positive" if active_users > total_users * 0.7 else "neutral"
59
- ),
60
- description="Currently active",
61
- ),
62
- StatCard(
63
- title="New This Week",
64
- value=f"{new_users_7d:,}",
65
- icon=Icons.PERSON_ADD,
66
- change_type="positive" if new_users_7d > 0 else "neutral",
67
- description="Last 7 days",
68
- ),
69
- StatCard(
70
- title="Staff Members",
71
- value=f"{staff_users:,}",
72
- icon=Icons.ADMIN_PANEL_SETTINGS,
73
- change=(
74
- f"{(staff_users/total_users*100):.1f}%" if total_users > 0 else "0%"
75
- ),
76
- change_type="neutral",
77
- description="Administrative access",
78
- ),
79
- ]
80
- except Exception as e:
81
- logger.error(f"Error getting user statistics: {e}")
82
- return [
83
- StatCard(
84
- title="Users",
85
- value="N/A",
86
- icon=Icons.PEOPLE,
87
- description="Data unavailable",
88
- )
89
- ]
90
-
91
- def get_support_statistics(self) -> List[StatCard]:
92
- """Get support ticket statistics as Pydantic models."""
93
- try:
94
- # Check if support is enabled
95
- if not self.is_support_enabled():
96
- return []
97
-
98
- from django_cfg.apps.support.models import Ticket
99
-
100
- total_tickets = Ticket.objects.count()
101
- open_tickets = Ticket.objects.filter(status='open').count()
102
- resolved_tickets = Ticket.objects.filter(status='resolved').count()
103
- new_tickets_7d = Ticket.objects.filter(
104
- created_at__gte=timezone.now() - timedelta(days=7)
105
- ).count()
106
-
107
- return [
108
- StatCard(
109
- title="Total Tickets",
110
- value=f"{total_tickets:,}",
111
- icon=Icons.SUPPORT_AGENT,
112
- change=f"+{new_tickets_7d}" if new_tickets_7d > 0 else None,
113
- change_type="positive" if new_tickets_7d > 0 else "neutral",
114
- description="All support tickets",
115
- ),
116
- StatCard(
117
- title="Open Tickets",
118
- value=f"{open_tickets:,}",
119
- icon=Icons.PENDING,
120
- change=(
121
- f"{(open_tickets/total_tickets*100):.1f}%"
122
- if total_tickets > 0
123
- else "0%"
124
- ),
125
- change_type=(
126
- "negative" if open_tickets > total_tickets * 0.3
127
- else "positive" if open_tickets == 0
128
- else "neutral"
129
- ),
130
- description="Awaiting response",
131
- ),
132
- StatCard(
133
- title="Resolved",
134
- value=f"{resolved_tickets:,}",
135
- icon=Icons.CHECK_CIRCLE,
136
- change=(
137
- f"{(resolved_tickets/total_tickets*100):.1f}%"
138
- if total_tickets > 0
139
- else "0%"
140
- ),
141
- change_type="positive",
142
- description="Successfully resolved",
143
- ),
144
- StatCard(
145
- title="New This Week",
146
- value=f"{new_tickets_7d:,}",
147
- icon=Icons.NEW_RELEASES,
148
- change_type="positive" if new_tickets_7d > 0 else "neutral",
149
- description="Last 7 days",
150
- ),
151
- ]
152
- except Exception as e:
153
- logger.error(f"Error getting support statistics: {e}")
154
- return [
155
- StatCard(
156
- title="Support",
157
- value="N/A",
158
- icon=Icons.SUPPORT_AGENT,
159
- description="Data unavailable",
160
- )
161
- ]
162
-
163
- def get_app_statistics(self) -> Dict[str, Any]:
164
- """Get statistics for all apps and their models."""
165
- stats = {"apps": {}, "total_records": 0, "total_models": 0, "total_apps": 0}
166
-
167
- # Get all installed apps
168
- for app_config in apps.get_app_configs():
169
- app_label = app_config.label
170
-
171
- # Skip system apps
172
- if app_label in ["admin", "contenttypes", "sessions", "auth"]:
173
- continue
174
-
175
- app_stats = self._get_app_stats(app_label)
176
- if app_stats:
177
- stats["apps"][app_label] = app_stats
178
- stats["total_records"] += app_stats.get("total_records", 0)
179
- stats["total_models"] += app_stats.get("model_count", 0)
180
- stats["total_apps"] += 1
181
-
182
- return stats
183
-
184
- def _get_app_stats(self, app_label: str) -> Dict[str, Any]:
185
- """Get statistics for a specific app."""
186
- try:
187
- app_config = apps.get_app_config(app_label)
188
- # Convert generator to list to avoid len() error
189
- models_list = list(app_config.get_models())
190
-
191
- if not models_list:
192
- return None
193
-
194
- app_stats = {
195
- "name": app_config.verbose_name or app_label.title(),
196
- "models": {},
197
- "total_records": 0,
198
- "model_count": len(models_list),
199
- }
200
-
201
- for model in models_list:
202
- try:
203
- # Get model statistics
204
- model_stats = self._get_model_stats(model)
205
- if model_stats:
206
- app_stats["models"][model._meta.model_name] = model_stats
207
- app_stats["total_records"] += model_stats.get("count", 0)
208
- except Exception:
209
- continue
210
-
211
- return app_stats
212
-
213
- except Exception:
214
- return None
215
-
216
- def _get_model_stats(self, model) -> Dict[str, Any]:
217
- """Get statistics for a specific model."""
218
- try:
219
- # Get basic model info
220
- model_stats = {
221
- "name": model._meta.verbose_name_plural
222
- or model._meta.verbose_name
223
- or model._meta.model_name,
224
- "count": model.objects.count(),
225
- "fields_count": len(model._meta.fields),
226
- "admin_url": f"admin:{model._meta.app_label}_{model._meta.model_name}_changelist",
227
- }
228
-
229
- return model_stats
230
-
231
- except Exception:
232
- return None
233
-
234
- def is_support_enabled(self) -> bool:
235
- """Check if support module is enabled."""
236
- try:
237
- from django_cfg.apps.support.models import Ticket
238
- return True
239
- except ImportError:
240
- return False