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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/centrifugo/views/__init__.py +0 -2
- django_cfg/apps/dashboard/permissions.py +48 -0
- django_cfg/apps/dashboard/serializers/__init__.py +8 -1
- django_cfg/apps/dashboard/serializers/commands.py +29 -0
- django_cfg/apps/dashboard/services/__init__.py +2 -0
- django_cfg/{modules/django_unfold/callbacks/base.py → apps/dashboard/services/commands_security.py} +28 -90
- django_cfg/apps/dashboard/services/commands_service.py +208 -9
- django_cfg/apps/dashboard/services/overview_service.py +205 -0
- django_cfg/apps/dashboard/views/commands_views.py +92 -4
- django_cfg/apps/frontend/test_routing.py +134 -0
- django_cfg/apps/frontend/views.py +73 -28
- django_cfg/apps/urls.py +0 -1
- django_cfg/core/builders/apps_builder.py +0 -58
- django_cfg/modules/django_unfold/__init__.py +5 -24
- django_cfg/modules/django_unfold/models/__init__.py +0 -23
- django_cfg/modules/django_unfold/models/config.py +11 -65
- django_cfg/modules/django_unfold/{dashboard.py → navigation.py} +21 -152
- django_cfg/modules/django_unfold/tailwind.py +2 -4
- django_cfg/pyproject.toml +1 -1
- django_cfg/registry/third_party.py +0 -9
- django_cfg/routing/callbacks.py +1 -43
- django_cfg/static/frontend/admin/404/index.html +1 -1
- django_cfg/static/frontend/admin/404.html +1 -1
- django_cfg/static/frontend/admin/500/index.html +1 -1
- django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_buildManifest.js +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{19430.fe7bff7372f8a256.js → 19430.c4c95603c23c17fe.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-9443faa6df24aebf.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/94141-bc6d47f419b26b21.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/{_app-c336f254967dd101.js → _app-c7dcd3aa616fab68.js} +6 -6
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-b39c7f22c066e2c6.js → cookies-97d279800f12aab4.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-5aedad0cf3a4f80f.js → privacy-1d5e6cd94689247e.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-dbd854d0d5d483e2.js → security-55e49700e7a01f5a.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-f3e1d2b9e5edf12f.js → terms-14c02bb2d3198352.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{centrifugo-22532c65971225eb.js → centrifugo-f9ecbc3ae0052a03.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-d4ccbe1265cbd853.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{webpack-da114020a6b940f5.js → webpack-5a92f81363b62aa7.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/css/3063068f0d5a8a00.css +3 -0
- django_cfg/static/frontend/admin/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/21350d82a1f187e9-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/auth/index.html +1 -1
- django_cfg/static/frontend/admin/index.html +1 -1
- django_cfg/static/frontend/admin/legal/cookies/index.html +1 -1
- django_cfg/static/frontend/admin/legal/privacy/index.html +1 -1
- django_cfg/static/frontend/admin/legal/security/index.html +1 -1
- django_cfg/static/frontend/admin/legal/terms/index.html +1 -1
- django_cfg/static/frontend/admin/private/centrifugo/index.html +1 -1
- django_cfg/static/frontend/admin/private/index.html +1 -1
- django_cfg/static/frontend/admin/private/profile/index.html +1 -1
- django_cfg/static/frontend/admin/private/ui/index.html +2 -2
- django_cfg/templates/admin/index.html +1 -1
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/METADATA +1 -1
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/RECORD +62 -163
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +0 -260
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +0 -313
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +0 -803
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +0 -341
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +0 -432
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +0 -33
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +0 -210
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +0 -46
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +0 -123
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +0 -45
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +0 -84
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/stat_cards.html +0 -53
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +0 -91
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/tab_navigation.html +0 -29
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +0 -415
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +0 -61
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +0 -58
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +0 -48
- django_cfg/apps/centrifugo/templatetags/__init__.py +0 -1
- django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +0 -81
- django_cfg/apps/centrifugo/urls_admin.py +0 -20
- django_cfg/apps/centrifugo/views/dashboard.py +0 -28
- django_cfg/modules/django_dashboard/__init__.py +0 -23
- django_cfg/modules/django_dashboard/components.py +0 -312
- django_cfg/modules/django_dashboard/debug.py +0 -174
- django_cfg/modules/django_dashboard/management/__init__.py +0 -0
- django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
- django_cfg/modules/django_dashboard/management/commands/debug_dashboard.py +0 -109
- django_cfg/modules/django_dashboard/sections/__init__.py +0 -1
- django_cfg/modules/django_dashboard/sections/base.py +0 -129
- django_cfg/modules/django_dashboard/sections/commands.py +0 -33
- django_cfg/modules/django_dashboard/sections/documentation.py +0 -393
- django_cfg/modules/django_dashboard/sections/overview.py +0 -398
- django_cfg/modules/django_dashboard/sections/stats.py +0 -48
- django_cfg/modules/django_dashboard/sections/system.py +0 -74
- django_cfg/modules/django_dashboard/sections/widgets.py +0 -222
- django_cfg/modules/django_unfold/callbacks/__init__.py +0 -9
- django_cfg/modules/django_unfold/callbacks/actions.py +0 -51
- django_cfg/modules/django_unfold/callbacks/apizones.py +0 -122
- django_cfg/modules/django_unfold/callbacks/charts.py +0 -223
- django_cfg/modules/django_unfold/callbacks/commands.py +0 -40
- django_cfg/modules/django_unfold/callbacks/main.py +0 -322
- django_cfg/modules/django_unfold/callbacks/statistics.py +0 -240
- django_cfg/modules/django_unfold/callbacks/system.py +0 -180
- django_cfg/modules/django_unfold/callbacks/users.py +0 -65
- django_cfg/modules/django_unfold/models/dashboard.py +0 -207
- django_cfg/modules/django_unfold/models/tabs.py +0 -26
- django_cfg/modules/django_unfold/models.py +0 -98
- django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +0 -102
- django_cfg/static/frontend/admin/_next/static/chunks/23004-faae121bbfecc163.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-48bd5701f62faf27.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-fe9faa86ecdb0ce6.js +0 -1
- django_cfg/static/frontend/admin/_next/static/css/5f9a37b6e6a72303.css +0 -3
- django_cfg/static/frontend/admin/_next/static/media/438aa629764e75f3-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/4c9affa5bc8f420e-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/51251f8b9793cdb3-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/875ae681bfde4580-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/cc978ac5ee68c2b6-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/e857b654a2caa584-s.woff2 +0 -0
- django_cfg/templates/admin/sections/commands_section.html +0 -5
- django_cfg/templates/admin/sections/documentation_section.html +0 -5
- django_cfg/templates/admin/sections/overview_section.html +0 -5
- django_cfg/templates/admin/sections/stats_section.html +0 -5
- django_cfg/templates/admin/sections/system_section.html +0 -5
- django_cfg/templates/admin/sections/widgets_section.html +0 -11
- django_cfg/templates/admin_old/components/action_grid.html +0 -49
- django_cfg/templates/admin_old/components/card.html +0 -50
- django_cfg/templates/admin_old/components/data_table.html +0 -67
- django_cfg/templates/admin_old/components/metric_card.html +0 -39
- django_cfg/templates/admin_old/components/modal.html +0 -58
- django_cfg/templates/admin_old/components/progress_bar.html +0 -20
- django_cfg/templates/admin_old/components/section_header.html +0 -26
- django_cfg/templates/admin_old/components/stat_item.html +0 -32
- django_cfg/templates/admin_old/components/stats_grid.html +0 -72
- django_cfg/templates/admin_old/components/status_badge.html +0 -28
- django_cfg/templates/admin_old/components/user_avatar.html +0 -27
- django_cfg/templates/admin_old/constance/change_list.html +0 -74
- django_cfg/templates/admin_old/constance/includes/default_value.html +0 -24
- django_cfg/templates/admin_old/constance/includes/fieldset_header.html +0 -15
- django_cfg/templates/admin_old/constance/includes/results_list.html +0 -16
- django_cfg/templates/admin_old/constance/includes/setting_row.html +0 -50
- django_cfg/templates/admin_old/constance/includes/table_headers.html +0 -10
- django_cfg/templates/admin_old/examples/component_class_example.html +0 -156
- django_cfg/templates/admin_old/import_export/change_list_export.html +0 -24
- django_cfg/templates/admin_old/import_export/change_list_import.html +0 -24
- django_cfg/templates/admin_old/import_export/change_list_import_export.html +0 -34
- django_cfg/templates/admin_old/index.html +0 -80
- django_cfg/templates/admin_old/index_new.html +0 -119
- django_cfg/templates/admin_old/layouts/base_dashboard.html +0 -62
- django_cfg/templates/admin_old/layouts/dashboard_with_tabs.html +0 -176
- django_cfg/templates/admin_old/sections/commands_section.html +0 -549
- django_cfg/templates/admin_old/sections/documentation_section.html +0 -152
- django_cfg/templates/admin_old/sections/overview_section.html +0 -112
- django_cfg/templates/admin_old/sections/stats_section.html +0 -35
- django_cfg/templates/admin_old/sections/system_section.html +0 -99
- django_cfg/templates/admin_old/sections/widgets_section.html +0 -129
- django_cfg/templates/admin_old/snippets/components/activity_tracker.html +0 -70
- django_cfg/templates/admin_old/snippets/components/charts_section.html +0 -113
- django_cfg/templates/admin_old/snippets/components/django_commands.html +0 -270
- django_cfg/templates/admin_old/snippets/components/quick_actions.html +0 -66
- django_cfg/templates/admin_old/snippets/components/recent_activity_improved.html +0 -25
- django_cfg/templates/admin_old/snippets/components/recent_users_table.html +0 -102
- django_cfg/templates/admin_old/snippets/components/stats_cards.html +0 -4
- django_cfg/templates/admin_old/snippets/components/stats_tiles.html +0 -92
- django_cfg/templates/admin_old/snippets/components/system_health.html +0 -22
- django_cfg/templates/admin_old/snippets/components/system_metrics.html +0 -199
- django_cfg/templates/admin_old/snippets/components/user_permissions.html +0 -57
- django_cfg/templates/admin_old/snippets/tabs/app_stats_tab.html +0 -201
- django_cfg/templates/admin_old/snippets/tabs/commands_tab.html +0 -114
- django_cfg/templates/admin_old/snippets/tabs/documentation_tab.html +0 -42
- django_cfg/templates/admin_old/snippets/tabs/overview_tab.html +0 -116
- django_cfg/templates/admin_old/snippets/tabs/stats_tab.html +0 -89
- django_cfg/templates/admin_old/snippets/tabs/users_tab.html +0 -51
- django_cfg/templates/admin_old/snippets/tabs/widgets_tab.html +0 -38
- django_cfg/templates/admin_old/snippets/zones/zones_table.html +0 -176
- /django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_ssgManifest.js +0 -0
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/entry_points.txt +0 -0
- {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
|