django-cfg 1.1.81__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.
Files changed (246) hide show
  1. django_cfg/__init__.py +20 -448
  2. django_cfg/apps/accounts/README.md +3 -3
  3. django_cfg/apps/accounts/admin/__init__.py +0 -2
  4. django_cfg/apps/accounts/admin/activity.py +2 -9
  5. django_cfg/apps/accounts/admin/filters.py +0 -42
  6. django_cfg/apps/accounts/admin/inlines.py +8 -8
  7. django_cfg/apps/accounts/admin/otp.py +5 -5
  8. django_cfg/apps/accounts/admin/registration_source.py +1 -8
  9. django_cfg/apps/accounts/admin/user.py +12 -20
  10. django_cfg/apps/accounts/managers/user_manager.py +2 -129
  11. django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
  12. django_cfg/apps/accounts/models.py +3 -123
  13. django_cfg/apps/accounts/serializers/otp.py +40 -44
  14. django_cfg/apps/accounts/serializers/profile.py +0 -2
  15. django_cfg/apps/accounts/services/otp_service.py +98 -186
  16. django_cfg/apps/accounts/signals.py +25 -15
  17. django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
  18. django_cfg/apps/accounts/views/otp.py +35 -36
  19. django_cfg/apps/agents/README.md +129 -0
  20. django_cfg/apps/agents/__init__.py +68 -0
  21. django_cfg/apps/agents/admin/__init__.py +17 -0
  22. django_cfg/apps/agents/admin/execution_admin.py +460 -0
  23. django_cfg/apps/agents/admin/registry_admin.py +360 -0
  24. django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
  25. django_cfg/apps/agents/apps.py +29 -0
  26. django_cfg/apps/agents/core/__init__.py +20 -0
  27. django_cfg/apps/agents/core/agent.py +281 -0
  28. django_cfg/apps/agents/core/dependencies.py +154 -0
  29. django_cfg/apps/agents/core/exceptions.py +66 -0
  30. django_cfg/apps/agents/core/models.py +106 -0
  31. django_cfg/apps/agents/core/orchestrator.py +391 -0
  32. django_cfg/apps/agents/examples/__init__.py +3 -0
  33. django_cfg/apps/agents/examples/simple_example.py +161 -0
  34. django_cfg/apps/agents/integration/__init__.py +14 -0
  35. django_cfg/apps/agents/integration/middleware.py +80 -0
  36. django_cfg/apps/agents/integration/registry.py +345 -0
  37. django_cfg/apps/agents/integration/signals.py +50 -0
  38. django_cfg/apps/agents/management/__init__.py +3 -0
  39. django_cfg/apps/agents/management/commands/__init__.py +3 -0
  40. django_cfg/apps/agents/management/commands/create_agent.py +365 -0
  41. django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
  42. django_cfg/apps/agents/managers/__init__.py +23 -0
  43. django_cfg/apps/agents/managers/execution.py +236 -0
  44. django_cfg/apps/agents/managers/registry.py +254 -0
  45. django_cfg/apps/agents/managers/toolsets.py +496 -0
  46. django_cfg/apps/agents/migrations/0001_initial.py +286 -0
  47. django_cfg/apps/agents/migrations/__init__.py +5 -0
  48. django_cfg/apps/agents/models/__init__.py +15 -0
  49. django_cfg/apps/agents/models/execution.py +215 -0
  50. django_cfg/apps/agents/models/registry.py +220 -0
  51. django_cfg/apps/agents/models/toolsets.py +305 -0
  52. django_cfg/apps/agents/patterns/__init__.py +24 -0
  53. django_cfg/apps/agents/patterns/content_agents.py +234 -0
  54. django_cfg/apps/agents/toolsets/__init__.py +15 -0
  55. django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
  56. django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
  57. django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
  58. django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
  59. django_cfg/apps/agents/urls.py +46 -0
  60. django_cfg/apps/knowbase/README.md +150 -0
  61. django_cfg/apps/knowbase/__init__.py +27 -0
  62. django_cfg/apps/knowbase/admin/__init__.py +23 -0
  63. django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
  64. django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
  65. django_cfg/apps/knowbase/admin/document_admin.py +650 -0
  66. django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
  67. django_cfg/apps/knowbase/apps.py +81 -0
  68. django_cfg/apps/knowbase/config/README.md +176 -0
  69. django_cfg/apps/knowbase/config/__init__.py +51 -0
  70. django_cfg/apps/knowbase/config/constance_fields.py +186 -0
  71. django_cfg/apps/knowbase/config/constance_settings.py +200 -0
  72. django_cfg/apps/knowbase/config/settings.py +444 -0
  73. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  74. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  75. django_cfg/apps/knowbase/management/__init__.py +0 -0
  76. django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
  77. django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
  78. django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
  79. django_cfg/apps/knowbase/managers/__init__.py +22 -0
  80. django_cfg/apps/knowbase/managers/archive.py +426 -0
  81. django_cfg/apps/knowbase/managers/base.py +32 -0
  82. django_cfg/apps/knowbase/managers/chat.py +141 -0
  83. django_cfg/apps/knowbase/managers/document.py +203 -0
  84. django_cfg/apps/knowbase/managers/external_data.py +471 -0
  85. django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
  86. django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
  87. django_cfg/apps/knowbase/migrations/__init__.py +5 -0
  88. django_cfg/apps/knowbase/mixins/__init__.py +15 -0
  89. django_cfg/apps/knowbase/mixins/config.py +108 -0
  90. django_cfg/apps/knowbase/mixins/creator.py +81 -0
  91. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  92. django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
  93. django_cfg/apps/knowbase/mixins/service.py +362 -0
  94. django_cfg/apps/knowbase/models/__init__.py +41 -0
  95. django_cfg/apps/knowbase/models/archive.py +599 -0
  96. django_cfg/apps/knowbase/models/base.py +58 -0
  97. django_cfg/apps/knowbase/models/chat.py +157 -0
  98. django_cfg/apps/knowbase/models/document.py +267 -0
  99. django_cfg/apps/knowbase/models/external_data.py +376 -0
  100. django_cfg/apps/knowbase/serializers/__init__.py +68 -0
  101. django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
  102. django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
  103. django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
  104. django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
  105. django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
  106. django_cfg/apps/knowbase/services/__init__.py +40 -0
  107. django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
  108. django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
  109. django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
  110. django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
  111. django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
  112. django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
  113. django_cfg/apps/knowbase/services/base.py +53 -0
  114. django_cfg/apps/knowbase/services/chat_service.py +239 -0
  115. django_cfg/apps/knowbase/services/document_service.py +144 -0
  116. django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
  117. django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
  118. django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
  119. django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
  120. django_cfg/apps/knowbase/services/embedding/models.py +229 -0
  121. django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
  122. django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
  123. django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
  124. django_cfg/apps/knowbase/services/search_service.py +293 -0
  125. django_cfg/apps/knowbase/signals/__init__.py +21 -0
  126. django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
  127. django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
  128. django_cfg/apps/knowbase/signals/document_signals.py +143 -0
  129. django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
  130. django_cfg/apps/knowbase/tasks/__init__.py +39 -0
  131. django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
  132. django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
  133. django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
  134. django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
  135. django_cfg/apps/knowbase/urls.py +43 -0
  136. django_cfg/apps/knowbase/utils/__init__.py +12 -0
  137. django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
  138. django_cfg/apps/knowbase/utils/text_processing.py +375 -0
  139. django_cfg/apps/knowbase/utils/validation.py +99 -0
  140. django_cfg/apps/knowbase/views/__init__.py +28 -0
  141. django_cfg/apps/knowbase/views/archive_views.py +469 -0
  142. django_cfg/apps/knowbase/views/base.py +49 -0
  143. django_cfg/apps/knowbase/views/chat_views.py +181 -0
  144. django_cfg/apps/knowbase/views/document_views.py +183 -0
  145. django_cfg/apps/knowbase/views/public_views.py +129 -0
  146. django_cfg/apps/leads/admin.py +70 -0
  147. django_cfg/apps/newsletter/admin.py +234 -0
  148. django_cfg/apps/newsletter/admin_filters.py +124 -0
  149. django_cfg/apps/support/admin.py +196 -0
  150. django_cfg/apps/support/admin_filters.py +71 -0
  151. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  152. django_cfg/apps/urls.py +5 -4
  153. django_cfg/cli/README.md +1 -1
  154. django_cfg/cli/commands/create_project.py +2 -2
  155. django_cfg/cli/commands/info.py +1 -1
  156. django_cfg/config.py +44 -0
  157. django_cfg/core/config.py +29 -82
  158. django_cfg/core/environment.py +1 -1
  159. django_cfg/core/generation.py +19 -107
  160. django_cfg/{integration.py → core/integration.py} +18 -16
  161. django_cfg/core/validation.py +1 -1
  162. django_cfg/management/__init__.py +1 -1
  163. django_cfg/management/commands/__init__.py +1 -1
  164. django_cfg/management/commands/auto_generate.py +482 -0
  165. django_cfg/management/commands/migrator.py +19 -101
  166. django_cfg/management/commands/test_email.py +1 -1
  167. django_cfg/middleware/README.md +0 -158
  168. django_cfg/middleware/__init__.py +0 -2
  169. django_cfg/middleware/user_activity.py +3 -3
  170. django_cfg/models/api.py +145 -0
  171. django_cfg/models/base.py +287 -0
  172. django_cfg/models/cache.py +4 -4
  173. django_cfg/models/constance.py +25 -88
  174. django_cfg/models/database.py +9 -9
  175. django_cfg/models/drf.py +3 -36
  176. django_cfg/models/email.py +163 -0
  177. django_cfg/models/environment.py +276 -0
  178. django_cfg/models/limits.py +1 -1
  179. django_cfg/models/logging.py +366 -0
  180. django_cfg/models/revolution.py +41 -2
  181. django_cfg/models/security.py +125 -0
  182. django_cfg/models/services.py +1 -1
  183. django_cfg/modules/__init__.py +2 -56
  184. django_cfg/modules/base.py +78 -52
  185. django_cfg/modules/django_currency/service.py +2 -2
  186. django_cfg/modules/django_email.py +2 -2
  187. django_cfg/modules/django_health.py +267 -0
  188. django_cfg/modules/django_llm/llm/client.py +79 -17
  189. django_cfg/modules/django_llm/translator/translator.py +2 -2
  190. django_cfg/modules/django_logger.py +2 -2
  191. django_cfg/modules/django_ngrok.py +2 -2
  192. django_cfg/modules/django_tasks.py +68 -3
  193. django_cfg/modules/django_telegram.py +3 -3
  194. django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
  195. django_cfg/modules/django_twilio/service.py +2 -2
  196. django_cfg/modules/django_twilio/simple_service.py +2 -2
  197. django_cfg/modules/django_twilio/templates/guide.md +266 -0
  198. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  199. django_cfg/modules/django_unfold/__init__.py +69 -0
  200. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  201. django_cfg/modules/django_unfold/dashboard.py +278 -0
  202. django_cfg/modules/django_unfold/icons/README.md +145 -0
  203. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  204. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  205. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  206. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  207. django_cfg/modules/django_unfold/models/config.py +601 -0
  208. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  209. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  210. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  211. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  212. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  213. django_cfg/modules/django_unfold/utils.py +140 -0
  214. django_cfg/registry/__init__.py +23 -0
  215. django_cfg/registry/core.py +61 -0
  216. django_cfg/registry/exceptions.py +11 -0
  217. django_cfg/registry/modules.py +12 -0
  218. django_cfg/registry/services.py +26 -0
  219. django_cfg/registry/third_party.py +52 -0
  220. django_cfg/routing/__init__.py +19 -0
  221. django_cfg/routing/callbacks.py +198 -0
  222. django_cfg/routing/routers.py +48 -0
  223. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  224. django_cfg/templatetags/__init__.py +0 -0
  225. django_cfg/templatetags/django_cfg.py +33 -0
  226. django_cfg/urls.py +33 -0
  227. django_cfg/utils/path_resolution.py +1 -1
  228. django_cfg/utils/smart_defaults.py +7 -61
  229. django_cfg/utils/toolkit.py +663 -0
  230. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
  231. django_cfg-1.2.0.dist-info/RECORD +441 -0
  232. django_cfg/apps/tasks/@docs/README.md +0 -195
  233. django_cfg/archive/django_sample.zip +0 -0
  234. django_cfg/models/unfold.py +0 -271
  235. django_cfg/modules/unfold/__init__.py +0 -29
  236. django_cfg/modules/unfold/dashboard.py +0 -318
  237. django_cfg/pyproject.toml +0 -370
  238. django_cfg/routers.py +0 -83
  239. django_cfg-1.1.81.dist-info/RECORD +0 -278
  240. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  241. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  242. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  243. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  244. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.1.81.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 BaseModule
15
+ from .. import BaseCfgModule
16
16
 
17
17
 
18
- class SystemMonitor(BaseModule):
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
+ }