django-cfg 1.1.82__py3-none-any.whl → 1.2.0__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.
- django_cfg/__init__.py +20 -448
- django_cfg/apps/accounts/README.md +3 -3
- django_cfg/apps/accounts/admin/__init__.py +0 -2
- django_cfg/apps/accounts/admin/activity.py +2 -9
- django_cfg/apps/accounts/admin/filters.py +0 -42
- django_cfg/apps/accounts/admin/inlines.py +8 -8
- django_cfg/apps/accounts/admin/otp.py +5 -5
- django_cfg/apps/accounts/admin/registration_source.py +1 -8
- django_cfg/apps/accounts/admin/user.py +12 -20
- django_cfg/apps/accounts/managers/user_manager.py +2 -129
- django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
- django_cfg/apps/accounts/models.py +3 -123
- django_cfg/apps/accounts/serializers/otp.py +40 -44
- django_cfg/apps/accounts/serializers/profile.py +0 -2
- django_cfg/apps/accounts/services/otp_service.py +98 -186
- django_cfg/apps/accounts/signals.py +25 -15
- django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
- django_cfg/apps/accounts/views/otp.py +35 -36
- django_cfg/apps/agents/README.md +129 -0
- django_cfg/apps/agents/__init__.py +68 -0
- django_cfg/apps/agents/admin/__init__.py +17 -0
- django_cfg/apps/agents/admin/execution_admin.py +460 -0
- django_cfg/apps/agents/admin/registry_admin.py +360 -0
- django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
- django_cfg/apps/agents/apps.py +29 -0
- django_cfg/apps/agents/core/__init__.py +20 -0
- django_cfg/apps/agents/core/agent.py +281 -0
- django_cfg/apps/agents/core/dependencies.py +154 -0
- django_cfg/apps/agents/core/exceptions.py +66 -0
- django_cfg/apps/agents/core/models.py +106 -0
- django_cfg/apps/agents/core/orchestrator.py +391 -0
- django_cfg/apps/agents/examples/__init__.py +3 -0
- django_cfg/apps/agents/examples/simple_example.py +161 -0
- django_cfg/apps/agents/integration/__init__.py +14 -0
- django_cfg/apps/agents/integration/middleware.py +80 -0
- django_cfg/apps/agents/integration/registry.py +345 -0
- django_cfg/apps/agents/integration/signals.py +50 -0
- django_cfg/apps/agents/management/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/create_agent.py +365 -0
- django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
- django_cfg/apps/agents/managers/__init__.py +23 -0
- django_cfg/apps/agents/managers/execution.py +236 -0
- django_cfg/apps/agents/managers/registry.py +254 -0
- django_cfg/apps/agents/managers/toolsets.py +496 -0
- django_cfg/apps/agents/migrations/0001_initial.py +286 -0
- django_cfg/apps/agents/migrations/__init__.py +5 -0
- django_cfg/apps/agents/models/__init__.py +15 -0
- django_cfg/apps/agents/models/execution.py +215 -0
- django_cfg/apps/agents/models/registry.py +220 -0
- django_cfg/apps/agents/models/toolsets.py +305 -0
- django_cfg/apps/agents/patterns/__init__.py +24 -0
- django_cfg/apps/agents/patterns/content_agents.py +234 -0
- django_cfg/apps/agents/toolsets/__init__.py +15 -0
- django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
- django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
- django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
- django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
- django_cfg/apps/agents/urls.py +46 -0
- django_cfg/apps/knowbase/README.md +150 -0
- django_cfg/apps/knowbase/__init__.py +27 -0
- django_cfg/apps/knowbase/admin/__init__.py +23 -0
- django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
- django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
- django_cfg/apps/knowbase/admin/document_admin.py +650 -0
- django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
- django_cfg/apps/knowbase/apps.py +81 -0
- django_cfg/apps/knowbase/config/README.md +176 -0
- django_cfg/apps/knowbase/config/__init__.py +51 -0
- django_cfg/apps/knowbase/config/constance_fields.py +186 -0
- django_cfg/apps/knowbase/config/constance_settings.py +200 -0
- django_cfg/apps/knowbase/config/settings.py +444 -0
- django_cfg/apps/knowbase/examples/__init__.py +3 -0
- django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
- django_cfg/apps/knowbase/management/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
- django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
- django_cfg/apps/knowbase/managers/__init__.py +22 -0
- django_cfg/apps/knowbase/managers/archive.py +426 -0
- django_cfg/apps/knowbase/managers/base.py +32 -0
- django_cfg/apps/knowbase/managers/chat.py +141 -0
- django_cfg/apps/knowbase/managers/document.py +203 -0
- django_cfg/apps/knowbase/managers/external_data.py +471 -0
- django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
- django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
- django_cfg/apps/knowbase/migrations/__init__.py +5 -0
- django_cfg/apps/knowbase/mixins/__init__.py +15 -0
- django_cfg/apps/knowbase/mixins/config.py +108 -0
- django_cfg/apps/knowbase/mixins/creator.py +81 -0
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
- django_cfg/apps/knowbase/mixins/service.py +362 -0
- django_cfg/apps/knowbase/models/__init__.py +41 -0
- django_cfg/apps/knowbase/models/archive.py +599 -0
- django_cfg/apps/knowbase/models/base.py +58 -0
- django_cfg/apps/knowbase/models/chat.py +157 -0
- django_cfg/apps/knowbase/models/document.py +267 -0
- django_cfg/apps/knowbase/models/external_data.py +376 -0
- django_cfg/apps/knowbase/serializers/__init__.py +68 -0
- django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
- django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
- django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
- django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
- django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
- django_cfg/apps/knowbase/services/__init__.py +40 -0
- django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
- django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
- django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
- django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
- django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
- django_cfg/apps/knowbase/services/base.py +53 -0
- django_cfg/apps/knowbase/services/chat_service.py +239 -0
- django_cfg/apps/knowbase/services/document_service.py +144 -0
- django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
- django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
- django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
- django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
- django_cfg/apps/knowbase/services/embedding/models.py +229 -0
- django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
- django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
- django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
- django_cfg/apps/knowbase/services/search_service.py +293 -0
- django_cfg/apps/knowbase/signals/__init__.py +21 -0
- django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
- django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
- django_cfg/apps/knowbase/signals/document_signals.py +143 -0
- django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
- django_cfg/apps/knowbase/tasks/__init__.py +39 -0
- django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
- django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
- django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
- django_cfg/apps/knowbase/urls.py +43 -0
- django_cfg/apps/knowbase/utils/__init__.py +12 -0
- django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
- django_cfg/apps/knowbase/utils/text_processing.py +375 -0
- django_cfg/apps/knowbase/utils/validation.py +99 -0
- django_cfg/apps/knowbase/views/__init__.py +28 -0
- django_cfg/apps/knowbase/views/archive_views.py +469 -0
- django_cfg/apps/knowbase/views/base.py +49 -0
- django_cfg/apps/knowbase/views/chat_views.py +181 -0
- django_cfg/apps/knowbase/views/document_views.py +183 -0
- django_cfg/apps/knowbase/views/public_views.py +129 -0
- django_cfg/apps/leads/admin.py +70 -0
- django_cfg/apps/newsletter/admin.py +234 -0
- django_cfg/apps/newsletter/admin_filters.py +124 -0
- django_cfg/apps/support/admin.py +196 -0
- django_cfg/apps/support/admin_filters.py +71 -0
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/apps/urls.py +5 -4
- django_cfg/cli/README.md +1 -1
- django_cfg/cli/commands/create_project.py +2 -2
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/config.py +44 -0
- django_cfg/core/config.py +29 -82
- django_cfg/core/environment.py +1 -1
- django_cfg/core/generation.py +19 -107
- django_cfg/{integration.py → core/integration.py} +18 -16
- django_cfg/core/validation.py +1 -1
- django_cfg/management/__init__.py +1 -1
- django_cfg/management/commands/__init__.py +1 -1
- django_cfg/management/commands/auto_generate.py +482 -0
- django_cfg/management/commands/migrator.py +19 -101
- django_cfg/management/commands/test_email.py +1 -1
- django_cfg/middleware/README.md +0 -158
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/api.py +145 -0
- django_cfg/models/base.py +287 -0
- django_cfg/models/cache.py +4 -4
- django_cfg/models/constance.py +25 -88
- django_cfg/models/database.py +9 -9
- django_cfg/models/drf.py +3 -36
- django_cfg/models/email.py +163 -0
- django_cfg/models/environment.py +276 -0
- django_cfg/models/limits.py +1 -1
- django_cfg/models/logging.py +366 -0
- django_cfg/models/revolution.py +41 -2
- django_cfg/models/security.py +125 -0
- django_cfg/models/services.py +1 -1
- django_cfg/modules/__init__.py +2 -56
- django_cfg/modules/base.py +78 -52
- django_cfg/modules/django_currency/service.py +2 -2
- django_cfg/modules/django_email.py +2 -2
- django_cfg/modules/django_health.py +267 -0
- django_cfg/modules/django_llm/llm/client.py +79 -17
- django_cfg/modules/django_llm/translator/translator.py +2 -2
- django_cfg/modules/django_logger.py +2 -2
- django_cfg/modules/django_ngrok.py +2 -2
- django_cfg/modules/django_tasks.py +68 -3
- django_cfg/modules/django_telegram.py +3 -3
- django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
- django_cfg/modules/django_twilio/service.py +2 -2
- django_cfg/modules/django_twilio/simple_service.py +2 -2
- django_cfg/modules/django_twilio/twilio_service.py +2 -2
- django_cfg/modules/django_unfold/__init__.py +69 -0
- django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
- django_cfg/modules/django_unfold/dashboard.py +278 -0
- django_cfg/modules/django_unfold/icons/README.md +145 -0
- django_cfg/modules/django_unfold/icons/__init__.py +12 -0
- django_cfg/modules/django_unfold/icons/constants.py +2851 -0
- django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
- django_cfg/modules/django_unfold/models/__init__.py +42 -0
- django_cfg/modules/django_unfold/models/config.py +601 -0
- django_cfg/modules/django_unfold/models/dashboard.py +206 -0
- django_cfg/modules/django_unfold/models/dropdown.py +40 -0
- django_cfg/modules/django_unfold/models/navigation.py +73 -0
- django_cfg/modules/django_unfold/models/tabs.py +25 -0
- django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
- django_cfg/modules/django_unfold/utils.py +140 -0
- django_cfg/registry/__init__.py +23 -0
- django_cfg/registry/core.py +61 -0
- django_cfg/registry/exceptions.py +11 -0
- django_cfg/registry/modules.py +12 -0
- django_cfg/registry/services.py +26 -0
- django_cfg/registry/third_party.py +52 -0
- django_cfg/routing/__init__.py +19 -0
- django_cfg/routing/callbacks.py +198 -0
- django_cfg/routing/routers.py +48 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
- django_cfg/templatetags/__init__.py +0 -0
- django_cfg/templatetags/django_cfg.py +33 -0
- django_cfg/urls.py +33 -0
- django_cfg/utils/path_resolution.py +1 -1
- django_cfg/utils/smart_defaults.py +7 -61
- django_cfg/utils/toolkit.py +663 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
- django_cfg-1.2.0.dist-info/RECORD +441 -0
- django_cfg/archive/django_sample.zip +0 -0
- django_cfg/models/unfold.py +0 -271
- django_cfg/modules/unfold/__init__.py +0 -29
- django_cfg/modules/unfold/dashboard.py +0 -318
- django_cfg/pyproject.toml +0 -370
- django_cfg/routers.py +0 -83
- django_cfg-1.1.82.dist-info/RECORD +0 -278
- /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
- /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
- /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
- /django_cfg/{version_check.py → utils/version_check.py} +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
"""
|
2
|
+
Dashboard Components Models for Unfold
|
3
|
+
|
4
|
+
Pydantic models for dashboard components like stat cards, health items, etc.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import List, Optional, Dict, Any, Literal
|
8
|
+
from pydantic import BaseModel, Field, ConfigDict, computed_field
|
9
|
+
|
10
|
+
|
11
|
+
class StatCard(BaseModel):
|
12
|
+
"""Dashboard statistics card model."""
|
13
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
14
|
+
|
15
|
+
title: str = Field(..., description="Card title")
|
16
|
+
value: str = Field(..., description="Main value to display")
|
17
|
+
icon: str = Field(..., description="Material icon name")
|
18
|
+
change: Optional[str] = Field(None, description="Change indicator (e.g., '+12%')")
|
19
|
+
change_type: Literal["positive", "negative", "neutral"] = Field(default="neutral", description="Change type")
|
20
|
+
description: Optional[str] = Field(None, description="Additional description")
|
21
|
+
color: str = Field("primary", description="Card color theme")
|
22
|
+
|
23
|
+
@computed_field
|
24
|
+
@property
|
25
|
+
def css_classes(self) -> Dict[str, str]:
|
26
|
+
"""Get CSS classes for different states."""
|
27
|
+
return {
|
28
|
+
"positive": "text-emerald-600 bg-emerald-100 dark:bg-emerald-900/20 dark:text-emerald-400",
|
29
|
+
"negative": "text-red-600 bg-red-100 dark:bg-red-900/20 dark:text-red-400",
|
30
|
+
"neutral": "text-slate-600 bg-slate-100 dark:bg-slate-700 dark:text-slate-400"
|
31
|
+
}
|
32
|
+
|
33
|
+
def to_dict(self) -> Dict[str, Any]:
|
34
|
+
"""Convert to dictionary for Unfold dashboard widgets."""
|
35
|
+
return {
|
36
|
+
"title": self.title,
|
37
|
+
"value_template": self.value,
|
38
|
+
"icon": self.icon,
|
39
|
+
"color": self.color,
|
40
|
+
"change": self.change,
|
41
|
+
"change_type": self.change_type,
|
42
|
+
"description": self.description,
|
43
|
+
}
|
44
|
+
|
45
|
+
|
46
|
+
class SystemHealthItem(BaseModel):
|
47
|
+
"""System health status item."""
|
48
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
49
|
+
|
50
|
+
component: str = Field(..., description="Component name")
|
51
|
+
status: Literal["healthy", "warning", "error", "unknown"] = Field(..., description="Health status")
|
52
|
+
description: str = Field(..., description="Status description")
|
53
|
+
last_check: str = Field(..., description="Last check time")
|
54
|
+
health_percentage: Optional[int] = Field(None, description="Health percentage (0-100)")
|
55
|
+
|
56
|
+
@computed_field
|
57
|
+
@property
|
58
|
+
def icon(self) -> str:
|
59
|
+
"""Get icon based on component type."""
|
60
|
+
icons = {
|
61
|
+
"database": "storage",
|
62
|
+
"cache": "memory",
|
63
|
+
"queue": "queue",
|
64
|
+
"storage": "folder",
|
65
|
+
"api": "api",
|
66
|
+
}
|
67
|
+
return icons.get(self.component.lower(), "info")
|
68
|
+
|
69
|
+
@computed_field
|
70
|
+
@property
|
71
|
+
def status_icon(self) -> str:
|
72
|
+
"""Get status icon."""
|
73
|
+
icons = {
|
74
|
+
"healthy": "check_circle",
|
75
|
+
"warning": "warning",
|
76
|
+
"error": "error",
|
77
|
+
"unknown": "help"
|
78
|
+
}
|
79
|
+
return icons.get(self.status, "help")
|
80
|
+
|
81
|
+
|
82
|
+
class QuickAction(BaseModel):
|
83
|
+
"""Quick action button model."""
|
84
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
85
|
+
|
86
|
+
title: str = Field(..., description="Action title")
|
87
|
+
description: str = Field(..., description="Action description")
|
88
|
+
icon: str = Field(..., description="Material icon name")
|
89
|
+
link: str = Field(..., description="Action URL or URL name")
|
90
|
+
color: Literal["primary", "success", "warning", "danger", "secondary"] = Field(default="primary", description="Button color theme")
|
91
|
+
category: str = Field("general", description="Action category (admin, user, system)")
|
92
|
+
|
93
|
+
def get_resolved_url(self) -> str:
|
94
|
+
"""
|
95
|
+
Resolve URL name to full URL if needed.
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
Full URL string - either the original link if it's already a URL,
|
99
|
+
or the resolved URL if it's a URL name.
|
100
|
+
"""
|
101
|
+
# If link starts with '/' or 'http', it's already a full URL
|
102
|
+
if self.link.startswith(("/", "http")):
|
103
|
+
return self.link
|
104
|
+
|
105
|
+
# Try to resolve as URL name
|
106
|
+
try:
|
107
|
+
from django.urls import reverse
|
108
|
+
from django.urls.exceptions import NoReverseMatch
|
109
|
+
return reverse(self.link)
|
110
|
+
except (NoReverseMatch, ImportError, Exception):
|
111
|
+
# If reverse fails, return the original link
|
112
|
+
return self.link
|
113
|
+
|
114
|
+
def model_dump(self, **kwargs) -> dict:
|
115
|
+
"""Override model_dump to include resolved URL."""
|
116
|
+
data = super().model_dump(**kwargs)
|
117
|
+
# Replace link with resolved URL
|
118
|
+
data["link"] = self.get_resolved_url()
|
119
|
+
return data
|
120
|
+
|
121
|
+
|
122
|
+
class DashboardWidget(BaseModel):
|
123
|
+
"""Dashboard widget configuration."""
|
124
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
125
|
+
|
126
|
+
title: str = Field(..., description="Widget title")
|
127
|
+
template: Optional[str] = Field(None, description="Custom template path")
|
128
|
+
callback: Optional[str] = Field(None, description="Callback function path")
|
129
|
+
width: int = Field(12, description="Widget width (1-12)")
|
130
|
+
order: int = Field(0, description="Widget order")
|
131
|
+
|
132
|
+
def to_dict(self) -> Dict[str, Any]:
|
133
|
+
"""Convert to dictionary for Unfold dashboard widgets."""
|
134
|
+
return {
|
135
|
+
"title": self.title,
|
136
|
+
"template": self.template,
|
137
|
+
"callback": self.callback,
|
138
|
+
"width": self.width,
|
139
|
+
"order": self.order,
|
140
|
+
}
|
141
|
+
|
142
|
+
|
143
|
+
class StatsCardsWidget(BaseModel):
|
144
|
+
"""Stats cards widget for dashboard."""
|
145
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
146
|
+
|
147
|
+
type: Literal["stats_cards"] = Field(default="stats_cards", description="Widget type")
|
148
|
+
title: str = Field(..., description="Widget title")
|
149
|
+
cards: List[StatCard] = Field(default_factory=list, description="Statistics cards")
|
150
|
+
|
151
|
+
def to_dict(self) -> Dict[str, Any]:
|
152
|
+
"""Convert to dictionary for Unfold dashboard widgets."""
|
153
|
+
return {
|
154
|
+
"type": self.type,
|
155
|
+
"title": self.title,
|
156
|
+
"cards": [card.to_dict() for card in self.cards],
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
class ChartDataset(BaseModel):
|
161
|
+
"""Chart dataset for dashboard charts."""
|
162
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
163
|
+
|
164
|
+
label: str = Field(..., description="Dataset label")
|
165
|
+
data: List[int] = Field(default_factory=list, description="Data points")
|
166
|
+
backgroundColor: str = Field(..., description="Background color")
|
167
|
+
borderColor: str = Field(..., description="Border color")
|
168
|
+
tension: float = Field(0.4, description="Line tension")
|
169
|
+
|
170
|
+
|
171
|
+
class ChartData(BaseModel):
|
172
|
+
"""Chart data structure."""
|
173
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
174
|
+
|
175
|
+
labels: List[str] = Field(default_factory=list, description="Chart labels")
|
176
|
+
datasets: List[ChartDataset] = Field(default_factory=list, description="Chart datasets")
|
177
|
+
|
178
|
+
|
179
|
+
class DashboardData(BaseModel):
|
180
|
+
"""Main dashboard data container."""
|
181
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
182
|
+
|
183
|
+
# Statistics cards
|
184
|
+
stat_cards: List[StatCard] = Field(default_factory=list, description="Dashboard statistics cards")
|
185
|
+
|
186
|
+
# System health
|
187
|
+
system_health: List[SystemHealthItem] = Field(default_factory=list, description="System health items")
|
188
|
+
|
189
|
+
# Quick actions
|
190
|
+
quick_actions: List[QuickAction] = Field(default_factory=list, description="Quick action buttons")
|
191
|
+
|
192
|
+
# Additional data
|
193
|
+
last_updated: str = Field(..., description="Last update timestamp")
|
194
|
+
environment: str = Field("development", description="Current environment")
|
195
|
+
|
196
|
+
@computed_field
|
197
|
+
@property
|
198
|
+
def total_users(self) -> int:
|
199
|
+
"""Get total users from stat cards."""
|
200
|
+
for card in self.stat_cards:
|
201
|
+
if "user" in card.title.lower():
|
202
|
+
try:
|
203
|
+
return int(card.value.replace(",", ""))
|
204
|
+
except (ValueError, AttributeError):
|
205
|
+
pass
|
206
|
+
return 0
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""
|
2
|
+
Site Dropdown Models for Unfold Dashboard
|
3
|
+
|
4
|
+
Pydantic models for site dropdown menu items.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Union, Callable
|
8
|
+
from pydantic import BaseModel, Field, ConfigDict, computed_field
|
9
|
+
from ..utils import auto_resolve_url
|
10
|
+
|
11
|
+
|
12
|
+
class SiteDropdownItem(BaseModel):
|
13
|
+
"""Site dropdown menu item configuration with automatic URL resolution."""
|
14
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
15
|
+
|
16
|
+
title: str = Field(..., min_length=1, description="Menu item title")
|
17
|
+
icon: str = Field(..., min_length=1, description="Material icon name")
|
18
|
+
link: str = Field(..., min_length=1, description="Link URL or URL name")
|
19
|
+
|
20
|
+
@computed_field
|
21
|
+
@property
|
22
|
+
def resolved_link(self) -> Union[str, Callable]:
|
23
|
+
"""Get the link resolved for Unfold (computed field to avoid recursion)."""
|
24
|
+
if self.link:
|
25
|
+
return auto_resolve_url(self.link)
|
26
|
+
return "#"
|
27
|
+
|
28
|
+
def get_link_for_unfold(self):
|
29
|
+
"""Get the link in the format expected by Unfold."""
|
30
|
+
return self.resolved_link
|
31
|
+
|
32
|
+
def to_dict(self) -> dict:
|
33
|
+
"""Convert to dictionary for Unfold admin."""
|
34
|
+
return {
|
35
|
+
"icon": self.icon,
|
36
|
+
"title": self.title,
|
37
|
+
"link": self.get_link_for_unfold(),
|
38
|
+
}
|
39
|
+
|
40
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
"""
|
2
|
+
Navigation Models for Unfold Dashboard
|
3
|
+
|
4
|
+
Pydantic models for navigation items and sections.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import List, Optional, Union, Callable
|
8
|
+
from pydantic import BaseModel, Field, ConfigDict, computed_field
|
9
|
+
from enum import Enum
|
10
|
+
from ..utils import auto_resolve_url
|
11
|
+
|
12
|
+
|
13
|
+
class NavigationItemType(str, Enum):
|
14
|
+
"""Navigation item types."""
|
15
|
+
LINK = "link"
|
16
|
+
SEPARATOR = "separator"
|
17
|
+
GROUP = "group"
|
18
|
+
|
19
|
+
|
20
|
+
class NavigationItem(BaseModel):
|
21
|
+
"""Single navigation item configuration with automatic URL resolution."""
|
22
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
23
|
+
|
24
|
+
title: str = Field(..., min_length=1, description="Navigation item title")
|
25
|
+
icon: Optional[str] = Field(None, description="Material icon name")
|
26
|
+
link: Optional[str] = Field(None, description="URL link or URL name")
|
27
|
+
badge: Optional[str] = Field(None, description="Badge callback function path")
|
28
|
+
permission: Optional[str] = Field(None, description="Permission callback function path")
|
29
|
+
type: NavigationItemType = Field(default=NavigationItemType.LINK, description="Item type")
|
30
|
+
|
31
|
+
@computed_field
|
32
|
+
@property
|
33
|
+
def resolved_link(self) -> Union[str, Callable]:
|
34
|
+
"""Get the link resolved for Unfold (computed field to avoid recursion)."""
|
35
|
+
if self.link:
|
36
|
+
return auto_resolve_url(self.link)
|
37
|
+
return "#"
|
38
|
+
|
39
|
+
def get_link_for_unfold(self):
|
40
|
+
"""Get the link in the format expected by Unfold."""
|
41
|
+
return self.resolved_link
|
42
|
+
|
43
|
+
def to_dict(self) -> dict:
|
44
|
+
"""Convert to dictionary for Unfold admin."""
|
45
|
+
return {
|
46
|
+
"title": self.title,
|
47
|
+
"icon": self.icon,
|
48
|
+
"link": self.get_link_for_unfold(),
|
49
|
+
"badge": self.badge,
|
50
|
+
"permission": self.permission,
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
class NavigationSection(BaseModel):
|
56
|
+
"""Navigation section configuration."""
|
57
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
58
|
+
|
59
|
+
title: str = Field(..., min_length=1, description="Section title")
|
60
|
+
separator: bool = Field(default=True, description="Show separator")
|
61
|
+
collapsible: bool = Field(default=True, description="Section is collapsible")
|
62
|
+
items: List[NavigationItem] = Field(default_factory=list, description="Navigation items")
|
63
|
+
|
64
|
+
def to_dict(self) -> dict:
|
65
|
+
"""Convert to dictionary for Unfold admin."""
|
66
|
+
return {
|
67
|
+
"title": self.title,
|
68
|
+
"separator": self.separator,
|
69
|
+
"collapsible": self.collapsible,
|
70
|
+
"items": [item.to_dict() for item in self.items],
|
71
|
+
}
|
72
|
+
|
73
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
"""
|
2
|
+
Tab Configuration Models for Unfold Dashboard
|
3
|
+
|
4
|
+
Pydantic models for tab configurations.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import List, Optional
|
8
|
+
from pydantic import BaseModel, Field, ConfigDict
|
9
|
+
|
10
|
+
|
11
|
+
class TabItem(BaseModel):
|
12
|
+
"""Tab item configuration."""
|
13
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
14
|
+
|
15
|
+
title: str = Field(..., min_length=1, description="Tab title")
|
16
|
+
link: str = Field(..., min_length=1, description="Tab URL")
|
17
|
+
permission: Optional[str] = Field(None, description="Permission callback")
|
18
|
+
|
19
|
+
|
20
|
+
class TabConfiguration(BaseModel):
|
21
|
+
"""Tab configuration for admin models."""
|
22
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
23
|
+
|
24
|
+
models: List[str] = Field(..., min_items=1, description="Model names for tab")
|
25
|
+
items: List[TabItem] = Field(..., min_items=1, description="Tab items")
|
@@ -12,10 +12,10 @@ from typing import Dict, Any, List, Optional
|
|
12
12
|
from django.db import connections
|
13
13
|
from django.utils import timezone
|
14
14
|
from django.contrib.auth import get_user_model
|
15
|
-
from .. import
|
15
|
+
from .. import BaseCfgModule
|
16
16
|
|
17
17
|
|
18
|
-
class SystemMonitor(
|
18
|
+
class SystemMonitor(BaseCfgModule):
|
19
19
|
"""
|
20
20
|
System monitoring module for Unfold dashboard.
|
21
21
|
|
@@ -0,0 +1,140 @@
|
|
1
|
+
"""
|
2
|
+
Utility functions for Django Unfold integration.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Callable, Union
|
6
|
+
import logging
|
7
|
+
from django.http import HttpRequest
|
8
|
+
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
def auto_resolve_url(url_name_or_path: str) -> Union[str, Callable[[HttpRequest], str]]:
|
13
|
+
"""
|
14
|
+
Automatically convert URL names to resolver functions.
|
15
|
+
|
16
|
+
If the string doesn't start with '/' or 'http', treat it as a URL name
|
17
|
+
and return a function that will resolve it at runtime.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
url_name_or_path: URL name (e.g., 'admin:app_model_changelist') or direct path
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
Direct URL string or callable that resolves URL name
|
24
|
+
"""
|
25
|
+
if not url_name_or_path or url_name_or_path.startswith(("/", "http")):
|
26
|
+
# It's already a direct URL, return as is
|
27
|
+
return url_name_or_path
|
28
|
+
|
29
|
+
# It's a URL name, create a resolver function
|
30
|
+
url_name = url_name_or_path
|
31
|
+
|
32
|
+
def resolve_url(request: HttpRequest) -> str:
|
33
|
+
try:
|
34
|
+
from django.urls import reverse
|
35
|
+
from django.urls.exceptions import NoReverseMatch
|
36
|
+
resolved = reverse(url_name)
|
37
|
+
logger.debug(f"Auto-resolved URL: {url_name} -> {resolved}")
|
38
|
+
return resolved
|
39
|
+
except (NoReverseMatch, Exception) as e:
|
40
|
+
logger.warning(f"Could not resolve URL '{url_name}': {e}")
|
41
|
+
return url_name
|
42
|
+
|
43
|
+
return resolve_url
|
44
|
+
|
45
|
+
|
46
|
+
def get_link_for_unfold(link: Union[str, Callable]) -> Union[str, Callable]:
|
47
|
+
"""
|
48
|
+
Get the link in the format expected by Unfold.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
link: Direct URL string or resolver function
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
Link in format expected by Unfold (string or callable)
|
55
|
+
"""
|
56
|
+
if callable(link):
|
57
|
+
# It's a resolver function, Unfold will call it with request
|
58
|
+
return link
|
59
|
+
else:
|
60
|
+
# It's a direct URL
|
61
|
+
return link
|
62
|
+
|
63
|
+
|
64
|
+
# Convenience functions for common admin URLs
|
65
|
+
def admin_changelist(app_label: str, model_name: str) -> Callable[[HttpRequest], str]:
|
66
|
+
"""Create URL resolver for admin changelist."""
|
67
|
+
return auto_resolve_url(f"admin:{app_label}_{model_name}_changelist")
|
68
|
+
|
69
|
+
|
70
|
+
def admin_add(app_label: str, model_name: str) -> Callable[[HttpRequest], str]:
|
71
|
+
"""Create URL resolver for admin add form."""
|
72
|
+
return auto_resolve_url(f"admin:{app_label}_{model_name}_add")
|
73
|
+
|
74
|
+
|
75
|
+
def admin_change(app_label: str, model_name: str, obj_id: str = "0") -> Callable[[HttpRequest], str]:
|
76
|
+
"""Create URL resolver for admin change form."""
|
77
|
+
return auto_resolve_url(f"admin:{app_label}_{model_name}_change/{obj_id}/")
|
78
|
+
|
79
|
+
|
80
|
+
def admin_model_url(model_class, action: str = "changelist") -> Callable[[HttpRequest], str]:
|
81
|
+
"""
|
82
|
+
Create URL resolver for any Django model admin action.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
model_class: Django model class
|
86
|
+
action: Admin action ('changelist', 'add', 'change', etc.)
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
URL resolver function
|
90
|
+
"""
|
91
|
+
try:
|
92
|
+
app_label = model_class._meta.app_label
|
93
|
+
model_name = model_class._meta.model_name
|
94
|
+
return auto_resolve_url(f"admin:{app_label}_{model_name}_{action}")
|
95
|
+
except Exception:
|
96
|
+
# Fallback to admin index
|
97
|
+
return auto_resolve_url("admin:index")
|
98
|
+
|
99
|
+
|
100
|
+
def user_admin_url() -> Callable[[HttpRequest], str]:
|
101
|
+
"""Create URL resolver for current AUTH_USER_MODEL admin changelist."""
|
102
|
+
try:
|
103
|
+
from django.contrib.auth import get_user_model
|
104
|
+
User = get_user_model()
|
105
|
+
return admin_model_url(User, "changelist")
|
106
|
+
except Exception:
|
107
|
+
# Fallback to admin index
|
108
|
+
return auto_resolve_url("admin:index")
|
109
|
+
|
110
|
+
|
111
|
+
def create_navigation_item(title: str, icon: str, model_class=None, url_name: str = None, direct_url: str = None):
|
112
|
+
"""
|
113
|
+
Create NavigationItem with automatic URL resolution.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
title: Navigation item title
|
117
|
+
icon: Material icon name
|
118
|
+
model_class: Django model class (for admin URLs)
|
119
|
+
url_name: Django URL name
|
120
|
+
direct_url: Direct URL path
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
NavigationItem with proper URL resolution
|
124
|
+
"""
|
125
|
+
from .models.navigation import NavigationItem
|
126
|
+
|
127
|
+
if model_class:
|
128
|
+
# Use model admin changelist
|
129
|
+
link = admin_model_url(model_class, "changelist")
|
130
|
+
elif url_name:
|
131
|
+
# Use URL name
|
132
|
+
link = auto_resolve_url(url_name)
|
133
|
+
elif direct_url:
|
134
|
+
# Use direct URL
|
135
|
+
link = direct_url
|
136
|
+
else:
|
137
|
+
# Fallback
|
138
|
+
link = "#"
|
139
|
+
|
140
|
+
return NavigationItem(title=title, icon=icon, link=link)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""
|
2
|
+
Django-CFG Import Registry
|
3
|
+
|
4
|
+
Organized import system for django-cfg components.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .core import CORE_REGISTRY
|
8
|
+
from .services import SERVICES_REGISTRY
|
9
|
+
from .third_party import THIRD_PARTY_REGISTRY
|
10
|
+
from .modules import MODULES_REGISTRY
|
11
|
+
from .exceptions import EXCEPTIONS_REGISTRY
|
12
|
+
|
13
|
+
# Combine all registries
|
14
|
+
DJANGO_CFG_REGISTRY = {
|
15
|
+
**CORE_REGISTRY,
|
16
|
+
**SERVICES_REGISTRY,
|
17
|
+
**THIRD_PARTY_REGISTRY,
|
18
|
+
**MODULES_REGISTRY,
|
19
|
+
**EXCEPTIONS_REGISTRY,
|
20
|
+
}
|
21
|
+
|
22
|
+
# Export all available names
|
23
|
+
__all__ = list(DJANGO_CFG_REGISTRY.keys())
|
@@ -0,0 +1,61 @@
|
|
1
|
+
"""
|
2
|
+
Core Django-CFG components registry.
|
3
|
+
"""
|
4
|
+
|
5
|
+
CORE_REGISTRY = {
|
6
|
+
# Core configuration
|
7
|
+
"DjangoConfig": ("django_cfg.core.config", "DjangoConfig"),
|
8
|
+
|
9
|
+
# Core exceptions
|
10
|
+
"ConfigurationError": ("django_cfg.core.exceptions", "ConfigurationError"),
|
11
|
+
"ValidationError": ("django_cfg.core.exceptions", "ValidationError"),
|
12
|
+
"DatabaseError": ("django_cfg.core.exceptions", "DatabaseError"),
|
13
|
+
"CacheError": ("django_cfg.core.exceptions", "CacheError"),
|
14
|
+
"EnvironmentError": ("django_cfg.core.exceptions", "EnvironmentError"),
|
15
|
+
|
16
|
+
# Core integration
|
17
|
+
"DjangoIntegration": ("django_cfg.core.integration", "DjangoIntegration"),
|
18
|
+
|
19
|
+
# Database models
|
20
|
+
"DatabaseConfig": ("django_cfg.models.database", "DatabaseConfig"),
|
21
|
+
|
22
|
+
# Cache models
|
23
|
+
"CacheConfig": ("django_cfg.models.cache", "CacheConfig"),
|
24
|
+
|
25
|
+
# Security models
|
26
|
+
"SecuritySettings": ("django_cfg.models.security", "SecuritySettings"),
|
27
|
+
|
28
|
+
# Logging models
|
29
|
+
"LoggingConfig": ("django_cfg.models.logging", "LoggingConfig"),
|
30
|
+
|
31
|
+
# Environment models
|
32
|
+
"EnvironmentConfig": ("django_cfg.models.environment", "EnvironmentConfig"),
|
33
|
+
|
34
|
+
# Limits models
|
35
|
+
"LimitsConfig": ("django_cfg.models.limits", "LimitsConfig"),
|
36
|
+
|
37
|
+
# JWT models
|
38
|
+
"JWTConfig": ("django_cfg.models.jwt", "JWTConfig"),
|
39
|
+
|
40
|
+
# Task and queue models
|
41
|
+
"TaskConfig": ("django_cfg.models.tasks", "TaskConfig"),
|
42
|
+
"DramatiqConfig": ("django_cfg.models.tasks", "DramatiqConfig"),
|
43
|
+
|
44
|
+
# Utils
|
45
|
+
"version_check": ("django_cfg.utils.version_check", "version_check"),
|
46
|
+
"toolkit": ("django_cfg.utils.toolkit", "toolkit"),
|
47
|
+
"ConfigToolkit": ("django_cfg.utils.toolkit", "ConfigToolkit"),
|
48
|
+
|
49
|
+
# Routing
|
50
|
+
"DynamicRouter": ("django_cfg.routing.routers", "DynamicRouter"),
|
51
|
+
"health_callback": ("django_cfg.routing.callbacks", "health_callback"),
|
52
|
+
|
53
|
+
# Health module
|
54
|
+
"HealthService": ("django_cfg.modules.django_health", "HealthService"),
|
55
|
+
|
56
|
+
# Library configuration
|
57
|
+
"LIB_NAME": ("django_cfg.config", "LIB_NAME"),
|
58
|
+
"LIB_SITE_URL": ("django_cfg.config", "LIB_SITE_URL"),
|
59
|
+
"LIB_HEALTH_URL": ("django_cfg.config", "LIB_HEALTH_URL"),
|
60
|
+
"get_default_dropdown_items": ("django_cfg.config", "get_default_dropdown_items"),
|
61
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
"""
|
2
|
+
Django-CFG exceptions registry.
|
3
|
+
"""
|
4
|
+
|
5
|
+
EXCEPTIONS_REGISTRY = {
|
6
|
+
# Core exceptions
|
7
|
+
"DjangoCfgException": ("django_cfg.core.exceptions", "DjangoCfgException"),
|
8
|
+
"ConfigurationError": ("django_cfg.core.exceptions", "ConfigurationError"),
|
9
|
+
"ValidationError": ("django_cfg.core.exceptions", "ValidationError"),
|
10
|
+
"EnvironmentError": ("django_cfg.core.exceptions", "EnvironmentError"),
|
11
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
Django-CFG modules and utilities registry.
|
3
|
+
"""
|
4
|
+
|
5
|
+
MODULES_REGISTRY = {
|
6
|
+
# URL integration
|
7
|
+
"add_django_cfg_urls": ("django_cfg.core.integration", "add_django_cfg_urls"),
|
8
|
+
"get_django_cfg_urls_info": ("django_cfg.core.integration", "get_django_cfg_urls_info"),
|
9
|
+
|
10
|
+
# Configuration utilities
|
11
|
+
"set_current_config": ("django_cfg.core.config", "set_current_config"),
|
12
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
Service integrations registry.
|
3
|
+
"""
|
4
|
+
|
5
|
+
SERVICES_REGISTRY = {
|
6
|
+
# Email services
|
7
|
+
"EmailConfig": ("django_cfg.models.services", "EmailConfig"),
|
8
|
+
"DjangoEmailService": ("django_cfg.modules.django_email", "DjangoEmailService"),
|
9
|
+
"send_email": ("django_cfg.modules.django_email", "send_email"),
|
10
|
+
"SendGridConfig": ("django_cfg.modules.django_twilio.models", "SendGridConfig"),
|
11
|
+
|
12
|
+
# Telegram services
|
13
|
+
"TelegramConfig": ("django_cfg.models.services", "TelegramConfig"),
|
14
|
+
"DjangoTelegram": ("django_cfg.modules.django_telegram", "DjangoTelegram"),
|
15
|
+
"send_telegram_message": ("django_cfg.modules.django_telegram", "send_telegram_message"),
|
16
|
+
"send_telegram_photo": ("django_cfg.modules.django_telegram", "send_telegram_photo"),
|
17
|
+
|
18
|
+
# Twilio services
|
19
|
+
"TwilioConfig": ("django_cfg.modules.django_twilio.models", "TwilioConfig"),
|
20
|
+
"TwilioVerifyConfig": ("django_cfg.modules.django_twilio.models", "TwilioVerifyConfig"),
|
21
|
+
"TwilioChannelType": ("django_cfg.modules.django_twilio.models", "TwilioChannelType"),
|
22
|
+
|
23
|
+
# Logging services
|
24
|
+
"DjangoLogger": ("django_cfg.modules.django_logger", "DjangoLogger"),
|
25
|
+
"get_logger": ("django_cfg.modules.django_logger", "get_logger"),
|
26
|
+
}
|