django-cfg 1.4.88__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/services/__init__.py +2 -0
- django_cfg/apps/dashboard/services/overview_service.py +205 -0
- django_cfg/apps/frontend/test_routing.py +134 -0
- django_cfg/apps/frontend/views.py +69 -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/{D_d9HRw5Yn7BRHAX5q89_ → 0sN9ktsgXH48ygtGSrhfu}/_buildManifest.js +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-9443faa6df24aebf.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/{_app-1c0fff0f59a6d683.js → _app-c7dcd3aa616fab68.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{centrifugo-44a8313fa040e9ad.js → centrifugo-f9ecbc3ae0052a03.js} +1 -1
- 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-1.4.88.dist-info → django_cfg-1.4.89.dist-info}/METADATA +1 -1
- {django_cfg-1.4.88.dist-info → django_cfg-1.4.89.dist-info}/RECORD +39 -143
- 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/base.py +0 -290
- 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/50314-5ec79b293c2283dd.js +0 -1
- 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/{D_d9HRw5Yn7BRHAX5q89_ → 0sN9ktsgXH48ygtGSrhfu}/_ssgManifest.js +0 -0
- {django_cfg-1.4.88.dist-info → django_cfg-1.4.89.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.88.dist-info → django_cfg-1.4.89.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.88.dist-info → django_cfg-1.4.89.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
"""Overview section for dashboard."""
|
|
2
|
-
|
|
3
|
-
from typing import Any, Dict, List
|
|
4
|
-
|
|
5
|
-
from .base import DataSection
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class OverviewSection(DataSection):
|
|
9
|
-
"""
|
|
10
|
-
Overview section showing key metrics and system status.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
template_name = "admin/sections/overview_section.html"
|
|
14
|
-
title = "System Overview"
|
|
15
|
-
icon = "dashboard"
|
|
16
|
-
|
|
17
|
-
def get_data(self) -> Dict[str, Any]:
|
|
18
|
-
"""Get overview data."""
|
|
19
|
-
from django_cfg.core.config import get_current_config
|
|
20
|
-
|
|
21
|
-
config = get_current_config()
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
'stats': self.get_key_stats(),
|
|
25
|
-
'system_health': self.get_system_health(),
|
|
26
|
-
'recent_activity': self.get_recent_activity(),
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
def get_context_data(self, **kwargs) -> Dict[str, Any]:
|
|
30
|
-
"""Add additional context for includes."""
|
|
31
|
-
import json
|
|
32
|
-
|
|
33
|
-
from django.utils.safestring import mark_safe
|
|
34
|
-
|
|
35
|
-
context = super().get_context_data(**kwargs)
|
|
36
|
-
|
|
37
|
-
# Get time range from kwargs (default: 7 days)
|
|
38
|
-
time_range = kwargs.get('time_range', 7)
|
|
39
|
-
|
|
40
|
-
# Add data needed by included components
|
|
41
|
-
context['system_metrics'] = self.get_system_metrics()
|
|
42
|
-
context['recent_users'] = self.get_recent_users()
|
|
43
|
-
context['recent_users_table'] = self.get_recent_users_table()
|
|
44
|
-
|
|
45
|
-
# Convert activity tracker to JSON for JavaScript
|
|
46
|
-
activity_data = self.get_activity_tracker()
|
|
47
|
-
context['activity_tracker'] = mark_safe(json.dumps(activity_data))
|
|
48
|
-
|
|
49
|
-
context['charts'] = self.get_charts_data(days=time_range)
|
|
50
|
-
|
|
51
|
-
# Quick actions come from kwargs (passed from callbacks)
|
|
52
|
-
if 'quick_actions' in kwargs:
|
|
53
|
-
context['quick_actions'] = kwargs['quick_actions']
|
|
54
|
-
|
|
55
|
-
# Navigation for time range filter
|
|
56
|
-
if 'navigation' in kwargs:
|
|
57
|
-
context['navigation'] = kwargs['navigation']
|
|
58
|
-
|
|
59
|
-
return context
|
|
60
|
-
|
|
61
|
-
def get_key_stats(self) -> Dict[str, Any]:
|
|
62
|
-
"""Get key statistics."""
|
|
63
|
-
from django.contrib.auth import get_user_model
|
|
64
|
-
from django.db import connection
|
|
65
|
-
|
|
66
|
-
User = get_user_model()
|
|
67
|
-
|
|
68
|
-
# Get database count
|
|
69
|
-
db_count = len(connection.settings_dict.get('DATABASES', {})) if hasattr(connection, 'settings_dict') else 1
|
|
70
|
-
|
|
71
|
-
# Get app count
|
|
72
|
-
from django.apps import apps
|
|
73
|
-
app_count = len(apps.get_app_configs())
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
'users': User.objects.count(),
|
|
77
|
-
'databases': db_count,
|
|
78
|
-
'apps': app_count,
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
def get_system_health(self) -> Dict[str, Any]:
|
|
82
|
-
"""Get system health metrics."""
|
|
83
|
-
import sys
|
|
84
|
-
|
|
85
|
-
import psutil
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
'python_version': f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
89
|
-
'cpu_percent': psutil.cpu_percent(interval=0.1),
|
|
90
|
-
'memory_percent': psutil.virtual_memory().percent,
|
|
91
|
-
'disk_percent': psutil.disk_usage('/').percent,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
def get_recent_activity(self) -> list:
|
|
95
|
-
"""Get recent activity items."""
|
|
96
|
-
# TODO: Implement activity tracking
|
|
97
|
-
return []
|
|
98
|
-
|
|
99
|
-
def get_system_metrics(self) -> Dict[str, Any]:
|
|
100
|
-
"""Get system metrics for progress bars."""
|
|
101
|
-
from django.core.cache import cache
|
|
102
|
-
from django.db import connection
|
|
103
|
-
|
|
104
|
-
metrics = {}
|
|
105
|
-
|
|
106
|
-
# Database metrics
|
|
107
|
-
try:
|
|
108
|
-
with connection.cursor() as cursor:
|
|
109
|
-
cursor.execute("SELECT 1")
|
|
110
|
-
metrics["database"] = {
|
|
111
|
-
"status": "healthy",
|
|
112
|
-
"type": connection.settings_dict.get('ENGINE', 'Unknown').split('.')[-1],
|
|
113
|
-
"health_percentage": 95,
|
|
114
|
-
"description": "Connection successful",
|
|
115
|
-
}
|
|
116
|
-
except Exception as e:
|
|
117
|
-
metrics["database"] = {
|
|
118
|
-
"status": "error",
|
|
119
|
-
"type": "Database",
|
|
120
|
-
"health_percentage": 0,
|
|
121
|
-
"description": f"Connection failed: {str(e)[:50]}",
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
# Cache metrics
|
|
125
|
-
try:
|
|
126
|
-
cache.set("health_check", "ok", 10)
|
|
127
|
-
cache_result = cache.get("health_check")
|
|
128
|
-
if cache_result == "ok":
|
|
129
|
-
metrics["cache"] = {
|
|
130
|
-
"status": "healthy",
|
|
131
|
-
"type": "Memory Cache",
|
|
132
|
-
"health_percentage": 90,
|
|
133
|
-
"description": "Cache working properly",
|
|
134
|
-
}
|
|
135
|
-
else:
|
|
136
|
-
metrics["cache"] = {
|
|
137
|
-
"status": "warning",
|
|
138
|
-
"type": "Memory Cache",
|
|
139
|
-
"health_percentage": 50,
|
|
140
|
-
"description": "Cache response delayed",
|
|
141
|
-
}
|
|
142
|
-
except Exception as e:
|
|
143
|
-
metrics["cache"] = {
|
|
144
|
-
"status": "error",
|
|
145
|
-
"type": "Memory Cache",
|
|
146
|
-
"health_percentage": 0,
|
|
147
|
-
"description": f"Cache error: {str(e)[:50]}",
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
# Storage metrics
|
|
151
|
-
try:
|
|
152
|
-
import shutil
|
|
153
|
-
total, used, free = shutil.disk_usage("/")
|
|
154
|
-
usage_percentage = (used / total) * 100
|
|
155
|
-
free_percentage = 100 - usage_percentage
|
|
156
|
-
|
|
157
|
-
# Status based on free space
|
|
158
|
-
if free_percentage > 20:
|
|
159
|
-
status = "healthy"
|
|
160
|
-
elif free_percentage > 10:
|
|
161
|
-
status = "warning"
|
|
162
|
-
else:
|
|
163
|
-
status = "error"
|
|
164
|
-
|
|
165
|
-
metrics["storage"] = {
|
|
166
|
-
"status": status,
|
|
167
|
-
"type": "Disk Space",
|
|
168
|
-
"health_percentage": int(free_percentage), # Show free space percentage
|
|
169
|
-
"description": f"{free_percentage:.1f}% free ({usage_percentage:.1f}% used)",
|
|
170
|
-
}
|
|
171
|
-
except Exception as e:
|
|
172
|
-
metrics["storage"] = {
|
|
173
|
-
"status": "error",
|
|
174
|
-
"type": "Disk Space",
|
|
175
|
-
"health_percentage": 0,
|
|
176
|
-
"description": f"Check failed: {str(e)[:50]}",
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
# API metrics
|
|
180
|
-
try:
|
|
181
|
-
from django.urls import get_resolver
|
|
182
|
-
resolver = get_resolver()
|
|
183
|
-
url_patterns = list(resolver.url_patterns)
|
|
184
|
-
|
|
185
|
-
metrics["api"] = {
|
|
186
|
-
"status": "healthy",
|
|
187
|
-
"type": "REST API",
|
|
188
|
-
"health_percentage": 100,
|
|
189
|
-
"description": f"{len(url_patterns)} URL patterns",
|
|
190
|
-
}
|
|
191
|
-
except Exception:
|
|
192
|
-
metrics["api"] = {
|
|
193
|
-
"status": "warning",
|
|
194
|
-
"type": "REST API",
|
|
195
|
-
"health_percentage": 50,
|
|
196
|
-
"description": "Unable to count URLs",
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return metrics
|
|
200
|
-
|
|
201
|
-
def get_recent_users(self) -> List[Dict[str, Any]]:
|
|
202
|
-
"""Get recent users for activity section."""
|
|
203
|
-
from django.contrib.auth import get_user_model
|
|
204
|
-
|
|
205
|
-
User = get_user_model()
|
|
206
|
-
|
|
207
|
-
try:
|
|
208
|
-
recent_users = User.objects.order_by('-date_joined')[:5]
|
|
209
|
-
return list(recent_users.values(
|
|
210
|
-
'id', 'username', 'email', 'is_active', 'date_joined'
|
|
211
|
-
))
|
|
212
|
-
except Exception:
|
|
213
|
-
return []
|
|
214
|
-
|
|
215
|
-
def get_recent_users_table(self) -> Dict[str, Any]:
|
|
216
|
-
"""Get recent users in table format for Unfold table component."""
|
|
217
|
-
from django.contrib.auth import get_user_model
|
|
218
|
-
|
|
219
|
-
User = get_user_model()
|
|
220
|
-
|
|
221
|
-
try:
|
|
222
|
-
recent_users = User.objects.order_by('-date_joined')[:10]
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
"headers": ["Username", "Email", "Status", "Staff", "Joined"],
|
|
226
|
-
"rows": [
|
|
227
|
-
[
|
|
228
|
-
user.username,
|
|
229
|
-
user.email,
|
|
230
|
-
"✅ Active" if user.is_active else "❌ Inactive",
|
|
231
|
-
"🛡️ Yes" if user.is_staff else "—",
|
|
232
|
-
user.date_joined.strftime('%Y-%m-%d %H:%M')
|
|
233
|
-
]
|
|
234
|
-
for user in recent_users
|
|
235
|
-
]
|
|
236
|
-
}
|
|
237
|
-
except Exception:
|
|
238
|
-
return {"headers": [], "rows": []}
|
|
239
|
-
|
|
240
|
-
def get_activity_tracker(self) -> List[Dict[str, Any]]:
|
|
241
|
-
"""
|
|
242
|
-
Get activity tracker data for GitHub-style heatmap.
|
|
243
|
-
|
|
244
|
-
Returns list of dicts with date and count for last 365 days.
|
|
245
|
-
"""
|
|
246
|
-
from datetime import timedelta
|
|
247
|
-
|
|
248
|
-
from django.contrib.auth import get_user_model
|
|
249
|
-
from django.utils import timezone
|
|
250
|
-
|
|
251
|
-
User = get_user_model()
|
|
252
|
-
|
|
253
|
-
try:
|
|
254
|
-
# Get activity for last 365 days
|
|
255
|
-
today = timezone.now().date()
|
|
256
|
-
activity_data = []
|
|
257
|
-
|
|
258
|
-
for days_ago in range(364, -1, -1): # 365 days, newest last
|
|
259
|
-
date = today - timedelta(days=days_ago)
|
|
260
|
-
|
|
261
|
-
# Count user registrations on this day
|
|
262
|
-
registrations = User.objects.filter(
|
|
263
|
-
date_joined__date=date
|
|
264
|
-
).count()
|
|
265
|
-
|
|
266
|
-
# Count logins on this day (if last_login exists)
|
|
267
|
-
logins = 0
|
|
268
|
-
if hasattr(User, 'last_login'):
|
|
269
|
-
logins = User.objects.filter(
|
|
270
|
-
last_login__date=date
|
|
271
|
-
).count()
|
|
272
|
-
|
|
273
|
-
# Total activity for the day
|
|
274
|
-
total_activity = registrations + logins
|
|
275
|
-
|
|
276
|
-
activity_data.append({
|
|
277
|
-
'date': date.isoformat(),
|
|
278
|
-
'count': total_activity,
|
|
279
|
-
'level': self._get_activity_level(total_activity),
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
return activity_data
|
|
283
|
-
|
|
284
|
-
except Exception:
|
|
285
|
-
# Return empty on error
|
|
286
|
-
return []
|
|
287
|
-
|
|
288
|
-
def _get_activity_level(self, count: int) -> int:
|
|
289
|
-
"""
|
|
290
|
-
Convert activity count to level (0-4) for heatmap colors.
|
|
291
|
-
|
|
292
|
-
0 = no activity (gray)
|
|
293
|
-
1 = low (light green)
|
|
294
|
-
2 = medium (green)
|
|
295
|
-
3 = high (dark green)
|
|
296
|
-
4 = very high (darkest green)
|
|
297
|
-
"""
|
|
298
|
-
if count == 0:
|
|
299
|
-
return 0
|
|
300
|
-
elif count <= 2:
|
|
301
|
-
return 1
|
|
302
|
-
elif count <= 5:
|
|
303
|
-
return 2
|
|
304
|
-
elif count <= 10:
|
|
305
|
-
return 3
|
|
306
|
-
else:
|
|
307
|
-
return 4
|
|
308
|
-
|
|
309
|
-
def get_charts_data(self, days: int = 7) -> Dict[str, Any]:
|
|
310
|
-
"""Get charts data for Analytics Overview.
|
|
311
|
-
|
|
312
|
-
Args:
|
|
313
|
-
days: Number of days to include in charts (default: 7)
|
|
314
|
-
"""
|
|
315
|
-
import json
|
|
316
|
-
from datetime import datetime, timedelta
|
|
317
|
-
|
|
318
|
-
from django.contrib.auth import get_user_model
|
|
319
|
-
from django.utils import timezone
|
|
320
|
-
|
|
321
|
-
User = get_user_model()
|
|
322
|
-
|
|
323
|
-
try:
|
|
324
|
-
# Get last N days of user registrations
|
|
325
|
-
today = timezone.now().date()
|
|
326
|
-
dates = [(today - timedelta(days=i)).strftime('%Y-%m-%d') for i in range(days-1, -1, -1)]
|
|
327
|
-
|
|
328
|
-
# Count users registered each day
|
|
329
|
-
registrations = []
|
|
330
|
-
for date_str in dates:
|
|
331
|
-
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
|
332
|
-
count = User.objects.filter(
|
|
333
|
-
date_joined__date=date_obj
|
|
334
|
-
).count()
|
|
335
|
-
registrations.append(count)
|
|
336
|
-
|
|
337
|
-
# Count active users each day (last login)
|
|
338
|
-
activity = []
|
|
339
|
-
for date_str in dates:
|
|
340
|
-
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
|
341
|
-
# Count users who logged in that day
|
|
342
|
-
count = User.objects.filter(
|
|
343
|
-
last_login__date=date_obj
|
|
344
|
-
).count() if hasattr(User, 'last_login') else 0
|
|
345
|
-
activity.append(count)
|
|
346
|
-
|
|
347
|
-
# Format for charts
|
|
348
|
-
day_labels = [d.split('-')[2] for d in dates] # Just day numbers
|
|
349
|
-
|
|
350
|
-
# Prepare chart data structures
|
|
351
|
-
user_reg_data = {
|
|
352
|
-
'labels': day_labels,
|
|
353
|
-
'datasets': [{
|
|
354
|
-
'label': 'New Users',
|
|
355
|
-
'data': registrations,
|
|
356
|
-
'backgroundColor': 'rgba(59, 130, 246, 0.5)',
|
|
357
|
-
'borderColor': 'rgb(59, 130, 246)',
|
|
358
|
-
'borderWidth': 2,
|
|
359
|
-
}]
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
user_activity_data = {
|
|
363
|
-
'labels': day_labels,
|
|
364
|
-
'datasets': [{
|
|
365
|
-
'label': 'Active Users',
|
|
366
|
-
'data': activity,
|
|
367
|
-
'backgroundColor': 'rgba(34, 197, 94, 0.5)',
|
|
368
|
-
'borderColor': 'rgb(34, 197, 94)',
|
|
369
|
-
'borderWidth': 2,
|
|
370
|
-
}]
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return {
|
|
374
|
-
# User Registrations chart
|
|
375
|
-
'user_registrations': {
|
|
376
|
-
'labels': day_labels,
|
|
377
|
-
'datasets': [{
|
|
378
|
-
'label': 'New Users',
|
|
379
|
-
'data': registrations,
|
|
380
|
-
}]
|
|
381
|
-
},
|
|
382
|
-
# JSON string for Unfold chart component (NO mark_safe - let Django escape quotes)
|
|
383
|
-
'user_registrations_json': json.dumps(user_reg_data),
|
|
384
|
-
|
|
385
|
-
# User Activity chart
|
|
386
|
-
'user_activity': {
|
|
387
|
-
'labels': day_labels,
|
|
388
|
-
'datasets': [{
|
|
389
|
-
'label': 'Active Users',
|
|
390
|
-
'data': activity,
|
|
391
|
-
}]
|
|
392
|
-
},
|
|
393
|
-
# JSON string for Unfold chart component (NO mark_safe - let Django escape quotes)
|
|
394
|
-
'user_activity_json': json.dumps(user_activity_data),
|
|
395
|
-
}
|
|
396
|
-
except Exception:
|
|
397
|
-
# Return empty chart data
|
|
398
|
-
return {}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"""Statistics section for dashboard."""
|
|
2
|
-
|
|
3
|
-
from typing import Any, Dict, List
|
|
4
|
-
|
|
5
|
-
from .base import DataSection
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class StatsSection(DataSection):
|
|
9
|
-
"""
|
|
10
|
-
Statistics section showing detailed metrics.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
template_name = "admin/sections/stats_section.html"
|
|
14
|
-
title = "Statistics"
|
|
15
|
-
icon = "analytics"
|
|
16
|
-
|
|
17
|
-
def get_data(self) -> Dict[str, Any]:
|
|
18
|
-
"""Get statistics data."""
|
|
19
|
-
return {
|
|
20
|
-
'app_stats': self.get_app_stats(),
|
|
21
|
-
'time_series': self.get_time_series(),
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
def get_app_stats(self) -> List[Dict[str, Any]]:
|
|
25
|
-
"""Get per-app statistics."""
|
|
26
|
-
from django.apps import apps
|
|
27
|
-
|
|
28
|
-
stats = []
|
|
29
|
-
|
|
30
|
-
for app_config in apps.get_app_configs():
|
|
31
|
-
if app_config.name.startswith('django.'):
|
|
32
|
-
continue
|
|
33
|
-
|
|
34
|
-
# Count models in app
|
|
35
|
-
models = [m for m in apps.get_models() if m._meta.app_label == app_config.label]
|
|
36
|
-
|
|
37
|
-
stats.append({
|
|
38
|
-
'name': app_config.verbose_name,
|
|
39
|
-
'label': app_config.label,
|
|
40
|
-
'models_count': len(models),
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
return stats
|
|
44
|
-
|
|
45
|
-
def get_time_series(self) -> Dict[str, Any]:
|
|
46
|
-
"""Get time series data for charts."""
|
|
47
|
-
# TODO: Implement time series data
|
|
48
|
-
return {}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"""System section for dashboard."""
|
|
2
|
-
|
|
3
|
-
from typing import Any, Dict
|
|
4
|
-
|
|
5
|
-
from .base import DataSection
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class SystemSection(DataSection):
|
|
9
|
-
"""
|
|
10
|
-
System management section.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
template_name = "admin/sections/system_section.html"
|
|
14
|
-
title = "System Management"
|
|
15
|
-
icon = "settings"
|
|
16
|
-
|
|
17
|
-
def get_data(self) -> Dict[str, Any]:
|
|
18
|
-
"""Get system data."""
|
|
19
|
-
return {
|
|
20
|
-
'health': self.get_health_check(),
|
|
21
|
-
'services': self.get_services_status(),
|
|
22
|
-
'configuration': self.get_configuration(),
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
def get_health_check(self) -> Dict[str, Any]:
|
|
26
|
-
"""Get health check status."""
|
|
27
|
-
import psutil
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
'status': 'healthy',
|
|
31
|
-
'checks': {
|
|
32
|
-
'database': self.check_database(),
|
|
33
|
-
'cache': self.check_cache(),
|
|
34
|
-
'disk_space': psutil.disk_usage('/').percent < 90,
|
|
35
|
-
'memory': psutil.virtual_memory().percent < 90,
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
def check_database(self) -> bool:
|
|
40
|
-
"""Check database connectivity."""
|
|
41
|
-
from django.db import connection
|
|
42
|
-
|
|
43
|
-
try:
|
|
44
|
-
connection.ensure_connection()
|
|
45
|
-
return True
|
|
46
|
-
except Exception:
|
|
47
|
-
return False
|
|
48
|
-
|
|
49
|
-
def check_cache(self) -> bool:
|
|
50
|
-
"""Check cache availability."""
|
|
51
|
-
from django.core.cache import cache
|
|
52
|
-
|
|
53
|
-
try:
|
|
54
|
-
cache.set('health_check', True, 1)
|
|
55
|
-
return cache.get('health_check') is True
|
|
56
|
-
except Exception:
|
|
57
|
-
return False
|
|
58
|
-
|
|
59
|
-
def get_services_status(self) -> Dict[str, Any]:
|
|
60
|
-
"""Get services status."""
|
|
61
|
-
# TODO: Implement services status check
|
|
62
|
-
return {}
|
|
63
|
-
|
|
64
|
-
def get_configuration(self) -> Dict[str, Any]:
|
|
65
|
-
"""Get system configuration."""
|
|
66
|
-
from django_cfg.core.config import get_current_config
|
|
67
|
-
|
|
68
|
-
config = get_current_config()
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
'debug': config.debug,
|
|
72
|
-
'env_mode': config.env_mode,
|
|
73
|
-
'project_name': config.project_name,
|
|
74
|
-
}
|