django-cfg 1.4.120__py3-none-any.whl → 1.5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (182) hide show
  1. django_cfg/__init__.py +8 -4
  2. django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
  3. django_cfg/apps/dashboard/TRANSACTION_FIX.md +73 -0
  4. django_cfg/apps/dashboard/serializers/__init__.py +0 -12
  5. django_cfg/apps/dashboard/serializers/activity.py +1 -1
  6. django_cfg/apps/dashboard/services/__init__.py +0 -2
  7. django_cfg/apps/dashboard/services/charts_service.py +4 -3
  8. django_cfg/apps/dashboard/services/statistics_service.py +11 -2
  9. django_cfg/apps/dashboard/services/system_health_service.py +64 -106
  10. django_cfg/apps/dashboard/urls.py +0 -2
  11. django_cfg/apps/dashboard/views/__init__.py +0 -2
  12. django_cfg/apps/dashboard/views/commands_views.py +3 -6
  13. django_cfg/apps/dashboard/views/overview_views.py +14 -13
  14. django_cfg/apps/grpc/__init__.py +9 -0
  15. django_cfg/apps/grpc/admin/__init__.py +11 -0
  16. django_cfg/apps/{tasks → grpc}/admin/config.py +32 -41
  17. django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
  18. django_cfg/apps/grpc/apps.py +28 -0
  19. django_cfg/apps/grpc/auth/__init__.py +9 -0
  20. django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
  21. django_cfg/apps/grpc/interceptors/__init__.py +19 -0
  22. django_cfg/apps/grpc/interceptors/errors.py +241 -0
  23. django_cfg/apps/grpc/interceptors/logging.py +270 -0
  24. django_cfg/apps/grpc/interceptors/metrics.py +306 -0
  25. django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
  26. django_cfg/apps/grpc/management/__init__.py +1 -0
  27. django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
  28. django_cfg/apps/grpc/managers/__init__.py +10 -0
  29. django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
  30. django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
  31. django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
  32. django_cfg/apps/grpc/models/__init__.py +9 -0
  33. django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
  34. django_cfg/apps/grpc/serializers/__init__.py +23 -0
  35. django_cfg/apps/grpc/serializers/health.py +18 -0
  36. django_cfg/apps/grpc/serializers/requests.py +18 -0
  37. django_cfg/apps/grpc/serializers/services.py +50 -0
  38. django_cfg/apps/grpc/serializers/stats.py +22 -0
  39. django_cfg/apps/grpc/services/__init__.py +16 -0
  40. django_cfg/apps/grpc/services/base.py +375 -0
  41. django_cfg/apps/grpc/services/discovery.py +415 -0
  42. django_cfg/apps/grpc/urls.py +23 -0
  43. django_cfg/apps/grpc/utils/__init__.py +13 -0
  44. django_cfg/apps/grpc/utils/proto_gen.py +423 -0
  45. django_cfg/apps/grpc/views/__init__.py +9 -0
  46. django_cfg/apps/grpc/views/monitoring.py +497 -0
  47. django_cfg/apps/knowbase/apps.py +2 -2
  48. django_cfg/apps/maintenance/admin/api_key_admin.py +7 -9
  49. django_cfg/apps/maintenance/admin/site_admin.py +5 -4
  50. django_cfg/apps/newsletter/admin/newsletter_admin.py +12 -11
  51. django_cfg/apps/payments/admin/balance_admin.py +26 -36
  52. django_cfg/apps/payments/admin/payment_admin.py +65 -85
  53. django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
  54. django_cfg/apps/rq/__init__.py +9 -0
  55. django_cfg/apps/rq/apps.py +80 -0
  56. django_cfg/apps/rq/management/__init__.py +1 -0
  57. django_cfg/apps/rq/management/commands/__init__.py +1 -0
  58. django_cfg/apps/rq/management/commands/rqscheduler.py +31 -0
  59. django_cfg/apps/rq/management/commands/rqstats.py +33 -0
  60. django_cfg/apps/rq/management/commands/rqworker.py +31 -0
  61. django_cfg/apps/rq/management/commands/rqworker_pool.py +27 -0
  62. django_cfg/apps/rq/serializers/__init__.py +40 -0
  63. django_cfg/apps/rq/serializers/health.py +60 -0
  64. django_cfg/apps/rq/serializers/job.py +100 -0
  65. django_cfg/apps/rq/serializers/queue.py +80 -0
  66. django_cfg/apps/rq/serializers/schedule.py +178 -0
  67. django_cfg/apps/rq/serializers/testing.py +139 -0
  68. django_cfg/apps/rq/serializers/worker.py +58 -0
  69. django_cfg/apps/rq/services/__init__.py +25 -0
  70. django_cfg/apps/rq/services/config_helper.py +233 -0
  71. django_cfg/apps/rq/services/models/README.md +417 -0
  72. django_cfg/apps/rq/services/models/__init__.py +30 -0
  73. django_cfg/apps/rq/services/models/event.py +123 -0
  74. django_cfg/apps/rq/services/models/job.py +99 -0
  75. django_cfg/apps/rq/services/models/queue.py +92 -0
  76. django_cfg/apps/rq/services/models/worker.py +104 -0
  77. django_cfg/apps/rq/services/rq_converters.py +183 -0
  78. django_cfg/apps/rq/tasks/__init__.py +23 -0
  79. django_cfg/apps/rq/tasks/demo_tasks.py +284 -0
  80. django_cfg/apps/rq/urls.py +54 -0
  81. django_cfg/apps/rq/views/__init__.py +19 -0
  82. django_cfg/apps/rq/views/jobs.py +882 -0
  83. django_cfg/apps/rq/views/monitoring.py +248 -0
  84. django_cfg/apps/rq/views/queues.py +261 -0
  85. django_cfg/apps/rq/views/schedule.py +400 -0
  86. django_cfg/apps/rq/views/testing.py +761 -0
  87. django_cfg/apps/rq/views/workers.py +195 -0
  88. django_cfg/apps/urls.py +13 -8
  89. django_cfg/config.py +106 -0
  90. django_cfg/core/base/config_model.py +16 -26
  91. django_cfg/core/builders/apps_builder.py +7 -11
  92. django_cfg/core/generation/integration_generators/__init__.py +3 -6
  93. django_cfg/core/generation/integration_generators/django_rq.py +80 -0
  94. django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
  95. django_cfg/core/generation/orchestrator.py +15 -15
  96. django_cfg/core/integration/display/startup.py +6 -20
  97. django_cfg/mixins/__init__.py +2 -0
  98. django_cfg/mixins/superadmin_api.py +59 -0
  99. django_cfg/models/__init__.py +3 -3
  100. django_cfg/models/api/grpc/__init__.py +59 -0
  101. django_cfg/models/api/grpc/config.py +364 -0
  102. django_cfg/models/django/__init__.py +3 -3
  103. django_cfg/models/django/django_rq.py +621 -0
  104. django_cfg/models/django/revolution_legacy.py +1 -1
  105. django_cfg/modules/base.py +19 -6
  106. django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
  107. django_cfg/modules/django_admin/config/background_task_config.py +4 -4
  108. django_cfg/modules/django_admin/utils/__init__.py +41 -3
  109. django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
  110. django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
  111. django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
  112. django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
  113. django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
  114. django_cfg/modules/django_admin/utils/html/badges.py +47 -0
  115. django_cfg/modules/django_admin/utils/html/base.py +167 -0
  116. django_cfg/modules/django_admin/utils/html/code.py +87 -0
  117. django_cfg/modules/django_admin/utils/html/composition.py +205 -0
  118. django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
  119. django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
  120. django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
  121. django_cfg/modules/django_admin/utils/html/progress.py +127 -0
  122. django_cfg/modules/django_admin/utils/html_builder.py +55 -408
  123. django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
  124. django_cfg/modules/django_unfold/navigation.py +21 -18
  125. django_cfg/pyproject.toml +4 -6
  126. django_cfg/registry/core.py +4 -7
  127. django_cfg/registry/modules.py +6 -0
  128. django_cfg/static/frontend/admin.zip +0 -0
  129. django_cfg/templates/admin/constance/includes/results_list.html +73 -0
  130. django_cfg/templates/admin/index.html +187 -62
  131. django_cfg/templatetags/django_cfg.py +61 -1
  132. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/METADATA +12 -4
  133. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/RECORD +140 -96
  134. django_cfg/apps/dashboard/permissions.py +0 -48
  135. django_cfg/apps/dashboard/serializers/django_q2.py +0 -50
  136. django_cfg/apps/dashboard/services/django_q2_service.py +0 -159
  137. django_cfg/apps/dashboard/views/django_q2_views.py +0 -79
  138. django_cfg/apps/tasks/__init__.py +0 -64
  139. django_cfg/apps/tasks/admin/__init__.py +0 -4
  140. django_cfg/apps/tasks/admin/task_log.py +0 -265
  141. django_cfg/apps/tasks/apps.py +0 -15
  142. django_cfg/apps/tasks/filters/__init__.py +0 -10
  143. django_cfg/apps/tasks/filters/task_log.py +0 -121
  144. django_cfg/apps/tasks/migrations/0001_initial.py +0 -196
  145. django_cfg/apps/tasks/migrations/0002_delete_tasklog.py +0 -16
  146. django_cfg/apps/tasks/models/__init__.py +0 -4
  147. django_cfg/apps/tasks/models/task_log.py +0 -246
  148. django_cfg/apps/tasks/serializers/__init__.py +0 -28
  149. django_cfg/apps/tasks/serializers/task_log.py +0 -249
  150. django_cfg/apps/tasks/services/__init__.py +0 -10
  151. django_cfg/apps/tasks/services/client/__init__.py +0 -7
  152. django_cfg/apps/tasks/services/client/client.py +0 -234
  153. django_cfg/apps/tasks/services/config_helper.py +0 -63
  154. django_cfg/apps/tasks/services/sync.py +0 -204
  155. django_cfg/apps/tasks/urls.py +0 -16
  156. django_cfg/apps/tasks/views/__init__.py +0 -10
  157. django_cfg/apps/tasks/views/task_log.py +0 -41
  158. django_cfg/apps/tasks/views/task_log_base.py +0 -41
  159. django_cfg/apps/tasks/views/task_log_overview.py +0 -100
  160. django_cfg/apps/tasks/views/task_log_related.py +0 -41
  161. django_cfg/apps/tasks/views/task_log_stats.py +0 -91
  162. django_cfg/apps/tasks/views/task_log_timeline.py +0 -81
  163. django_cfg/core/generation/integration_generators/django_q2.py +0 -133
  164. django_cfg/core/generation/integration_generators/tasks.py +0 -88
  165. django_cfg/models/django/django_q2.py +0 -514
  166. django_cfg/models/tasks/__init__.py +0 -49
  167. django_cfg/models/tasks/backends.py +0 -122
  168. django_cfg/models/tasks/config.py +0 -209
  169. django_cfg/models/tasks/utils.py +0 -162
  170. django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
  171. django_cfg/modules/django_q2/README.md +0 -140
  172. django_cfg/modules/django_q2/__init__.py +0 -8
  173. django_cfg/modules/django_q2/apps.py +0 -107
  174. django_cfg/modules/django_q2/management/commands/__init__.py +0 -0
  175. django_cfg/modules/django_q2/management/commands/sync_django_q_schedules.py +0 -74
  176. /django_cfg/apps/{tasks/migrations → grpc/management/commands}/__init__.py +0 -0
  177. /django_cfg/{modules/django_q2/management → apps/grpc/migrations}/__init__.py +0 -0
  178. /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
  179. /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
  180. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/WHEEL +0 -0
  181. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/entry_points.txt +0 -0
  182. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,419 +1,66 @@
1
1
  """
2
- Universal HTML builder for Django Admin display methods.
3
- """
2
+ HtmlBuilder - Facade class for backward compatibility.
4
3
 
5
- from pathlib import Path
6
- from typing import Any, List, Optional, Union
7
- import re
4
+ This class provides the old `self.html.*` API by delegating to new modular components.
5
+ All existing admin code using `self.html.badge()`, `self.html.inline()`, etc. will continue to work.
8
6
 
9
- from django.utils.html import escape, format_html
10
- from django.utils.safestring import SafeString
7
+ New code should import and use the specific modules directly:
8
+ from django_cfg.modules.django_admin.utils import BaseElements, FormattingElements, etc.
9
+ """
11
10
 
12
- from ..icons import Icons
13
- from .markdown_renderer import MarkdownRenderer
11
+ from .html import (
12
+ BadgeElements,
13
+ BaseElements,
14
+ CodeElements,
15
+ CompositionElements,
16
+ FormattingElements,
17
+ KeyValueElements,
18
+ MarkdownIntegration,
19
+ ProgressElements,
20
+ )
14
21
 
15
22
 
16
23
  class HtmlBuilder:
17
24
  """
18
- Universal HTML builder with Material Icons support and Markdown rendering.
19
-
20
- Usage in admin methods:
21
- def stats(self, obj):
22
- return self.html.inline([
23
- self.html.icon_text(Icons.EDIT, obj.posts_count),
24
- self.html.icon_text(Icons.CHAT, obj.comments_count),
25
- ])
25
+ Facade class that delegates to modular HTML utilities.
26
26
 
27
- def documentation(self, obj):
28
- return self.html.markdown_docs(obj.docs_path)
27
+ Provides backward compatibility for `self.html.*` API in admin classes.
29
28
  """
30
29
 
31
- @staticmethod
32
- def icon(icon_name: str, size: str = "xs", css_class: str = "") -> SafeString:
33
- """
34
- Render Material Icon.
35
-
36
- Args:
37
- icon_name: Icon name from Icons class
38
- size: xs, sm, base, lg, xl
39
- css_class: Additional CSS classes
40
- """
41
- size_classes = {
42
- 'xs': 'text-xs',
43
- 'sm': 'text-sm',
44
- 'base': 'text-base',
45
- 'lg': 'text-lg',
46
- 'xl': 'text-xl'
47
- }
48
- size_class = size_classes.get(size, 'text-xs')
49
- classes = f"material-symbols-outlined {size_class}"
50
- if css_class:
51
- classes += f" {css_class}"
52
-
53
- return format_html('<span class="{}">{}</span>', classes, icon_name)
54
-
55
- @staticmethod
56
- def icon_text(icon_or_text: Union[str, Any], text: Any = None,
57
- icon_size: str = "xs", separator: str = " ") -> SafeString:
58
- """
59
- Render icon with text or emoji with text.
60
-
61
- Args:
62
- icon_or_text: Icon from Icons class, emoji, or text if text param is None
63
- text: Optional text to display after icon
64
- icon_size: Icon size (xs, sm, base, lg, xl)
65
- separator: Separator between icon and text
66
-
67
- Usage:
68
- html.icon_text(Icons.EDIT, 5) # Icon with number
69
- html.icon_text("📝", 5) # Emoji with number
70
- html.icon_text("Active") # Just text
71
- """
72
- if text is None:
73
- # Just text
74
- return format_html('<span>{}</span>', escape(str(icon_or_text)))
75
-
76
- # Check if it's a Material Icon (from Icons class) or emoji
77
- icon_str = str(icon_or_text)
78
-
79
- # Detect if it's emoji by checking for non-ASCII characters
80
- is_emoji = any(ord(c) > 127 for c in icon_str)
81
-
82
- if is_emoji or icon_str in ['📝', '💬', '🛒', '👤', '📧', '🔔', '⚙️', '🔧', '📊', '🎯']:
83
- # Emoji
84
- icon_html = escape(icon_str)
85
- else:
86
- # Material Icon
87
- icon_html = HtmlBuilder.icon(icon_str, size=icon_size)
88
-
89
- return format_html('{}{}<span>{}</span>', icon_html, separator, escape(str(text)))
90
-
91
- @staticmethod
92
- def inline(items: List[Any], separator: str = " | ",
93
- size: str = "small", css_class: str = "") -> SafeString:
94
- """
95
- Render items inline with separator.
96
-
97
- Args:
98
- items: List of SafeString/str items to join
99
- separator: Separator between items
100
- size: small, medium, large
101
- css_class: Additional CSS classes
102
-
103
- Usage:
104
- html.inline([
105
- html.icon_text(Icons.EDIT, 5),
106
- html.icon_text(Icons.CHAT, 10),
107
- ])
108
- """
109
- if not items:
110
- return format_html('<span class="text-font-subtle-light dark:text-font-subtle-dark">—</span>')
111
-
112
- size_classes = {
113
- 'small': 'text-xs',
114
- 'medium': 'text-sm',
115
- 'large': 'text-base'
116
- }
117
- size_class = size_classes.get(size, 'text-xs')
118
-
119
- classes = size_class
120
- if css_class:
121
- classes += f" {css_class}"
122
-
123
- # Convert items to strings, keeping SafeString as-is
124
- from django.utils.safestring import SafeString, mark_safe
125
- processed_items = []
126
- for item in items:
127
- if isinstance(item, (SafeString, str)):
128
- processed_items.append(item)
129
- else:
130
- processed_items.append(escape(str(item)))
131
-
132
- # Join with separator
133
- joined = mark_safe(separator.join(str(item) for item in processed_items))
134
-
135
- return format_html('<span class="{}">{}</span>', classes, joined)
136
-
137
- @staticmethod
138
- def span(text: Any, css_class: str = "") -> SafeString:
139
- """
140
- Render text in span with optional CSS class.
141
-
142
- Args:
143
- text: Text to display
144
- css_class: CSS classes
145
- """
146
- if css_class:
147
- return format_html('<span class="{}">{}</span>', css_class, escape(str(text)))
148
- return format_html('<span>{}</span>', escape(str(text)))
149
-
150
- @staticmethod
151
- def div(content: Any, css_class: str = "") -> SafeString:
152
- """
153
- Render content in div with optional CSS class.
154
-
155
- Args:
156
- content: Content to display (can be SafeString)
157
- css_class: CSS classes
158
- """
159
- if css_class:
160
- return format_html('<div class="{}">{}</div>', css_class, content)
161
- return format_html('<div>{}</div>', content)
162
-
163
- @staticmethod
164
- def link(url: str, text: str, css_class: str = "", target: str = "") -> SafeString:
165
- """
166
- Render link.
167
-
168
- Args:
169
- url: URL
170
- text: Link text
171
- css_class: CSS classes
172
- target: Target attribute (_blank, _self, etc)
173
- """
174
- if target:
175
- return format_html(
176
- '<a href="{}" class="{}" target="{}">{}</a>',
177
- url, css_class, target, escape(text)
178
- )
179
- return format_html('<a href="{}" class="{}">{}</a>', url, css_class, escape(text))
180
-
181
- @staticmethod
182
- def badge(text: Any, variant: str = "primary", icon: Optional[str] = None) -> SafeString:
183
- """
184
- Render badge with optional icon.
185
-
186
- Args:
187
- text: Badge text
188
- variant: primary, success, warning, danger, info, secondary
189
- icon: Optional Material Icon
190
-
191
- Usage:
192
- html.badge("Active", variant="success", icon=Icons.CHECK_CIRCLE)
193
- """
194
- variant_classes = {
195
- 'success': 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200',
196
- 'warning': 'bg-warning-100 text-warning-800 dark:bg-warning-900 dark:text-warning-200',
197
- 'danger': 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200',
198
- 'info': 'bg-info-100 text-info-800 dark:bg-info-900 dark:text-info-200',
199
- 'primary': 'bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200',
200
- 'secondary': 'bg-base-100 text-font-default-light dark:bg-base-800 dark:text-font-default-dark',
201
- }
202
-
203
- css_classes = variant_classes.get(variant, variant_classes['primary'])
204
-
205
- icon_html = ""
206
- if icon:
207
- icon_html = format_html('<span class="material-symbols-outlined text-xs mr-1">{}</span>', icon)
208
-
209
- return format_html(
210
- '<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {}">{}{}</span>',
211
- css_classes, icon_html, escape(str(text))
212
- )
213
-
214
- @staticmethod
215
- def empty(text: str = "—") -> SafeString:
216
- """Render empty/placeholder value."""
217
- return format_html(
218
- '<span class="text-font-subtle-light dark:text-font-subtle-dark">{}</span>',
219
- escape(text)
220
- )
221
-
222
- @staticmethod
223
- def code(text: Any, css_class: str = "") -> SafeString:
224
- """
225
- Render inline code.
226
-
227
- Args:
228
- text: Code text
229
- css_class: Additional CSS classes
230
-
231
- Usage:
232
- html.code("/path/to/file")
233
- html.code("command --arg value")
234
- """
235
- base_classes = "font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded"
236
- classes = f"{base_classes} {css_class}".strip()
237
-
238
- return format_html(
239
- '<code class="{}">{}</code>',
240
- classes,
241
- escape(str(text))
242
- )
243
-
244
- @staticmethod
245
- def code_block(
246
- text: Any,
247
- language: Optional[str] = None,
248
- max_height: Optional[str] = None,
249
- variant: str = "default"
250
- ) -> SafeString:
251
- """
252
- Render code block with optional syntax highlighting and scrolling.
253
-
254
- Args:
255
- text: Code content
256
- language: Programming language (json, python, bash, etc.) - for future syntax highlighting
257
- max_height: Max height with scrolling (e.g., "400px", "20rem")
258
- variant: Color variant - default, warning, danger, success, info
259
-
260
- Usage:
261
- html.code_block(json.dumps(data, indent=2), language="json")
262
- html.code_block(stdout, max_height="400px")
263
- html.code_block(stderr, max_height="400px", variant="warning")
264
- """
265
- # Variant-specific styles
266
- variant_classes = {
267
- 'default': 'bg-base-50 dark:bg-base-900 border-base-200 dark:border-base-700',
268
- 'warning': 'bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-700',
269
- 'danger': 'bg-danger-50 dark:bg-danger-900/20 border-danger-200 dark:border-danger-700',
270
- 'success': 'bg-success-50 dark:bg-success-900/20 border-success-200 dark:border-success-700',
271
- 'info': 'bg-info-50 dark:bg-info-900/20 border-info-200 dark:border-info-700',
272
- }
273
-
274
- variant_class = variant_classes.get(variant, variant_classes['default'])
275
-
276
- # Base styles
277
- base_classes = f"font-mono text-xs whitespace-pre-wrap break-words border rounded-md p-3 {variant_class}"
278
-
279
- # Add max-height and overflow if specified
280
- style = ""
281
- if max_height:
282
- style = f'style="max-height: {max_height}; overflow-y: auto;"'
283
-
284
- # Add language class for potential syntax highlighting
285
- lang_class = f"language-{language}" if language else ""
286
-
287
- return format_html(
288
- '<pre class="{} {}" {}><code>{}</code></pre>',
289
- base_classes,
290
- lang_class,
291
- style,
292
- escape(str(text))
293
- )
294
-
295
- @staticmethod
296
- def markdown(
297
- text: str,
298
- css_class: str = "",
299
- max_height: Optional[str] = None,
300
- enable_plugins: bool = True
301
- ) -> SafeString:
302
- """
303
- Render markdown text to beautifully styled HTML.
304
-
305
- Delegates to MarkdownRenderer.render_markdown() for actual rendering.
306
-
307
- Args:
308
- text: Markdown content
309
- css_class: Additional CSS classes
310
- max_height: Max height with scrolling (e.g., "400px", "20rem")
311
- enable_plugins: Enable mistune plugins (tables, strikethrough, etc.)
312
-
313
- Usage:
314
- # Simple markdown rendering
315
- html.markdown("# Hello\\n\\nThis is **bold** text")
316
-
317
- # With custom styling
318
- html.markdown(obj.description, css_class="my-custom-class")
319
-
320
- # With max height for long content
321
- html.markdown(obj.documentation, max_height="500px")
322
-
323
- Returns:
324
- SafeString with rendered HTML
325
- """
326
- return MarkdownRenderer.render_markdown(
327
- text=text,
328
- css_class=css_class,
329
- max_height=max_height,
330
- enable_plugins=enable_plugins
331
- )
332
-
333
- @staticmethod
334
- def uuid_short(uuid_value: Any, length: int = 6, show_tooltip: bool = True) -> SafeString:
335
- """
336
- Shorten UUID to first N characters with optional tooltip.
337
-
338
- Args:
339
- uuid_value: UUID string or UUID object
340
- length: Number of characters to show (default: 6)
341
- show_tooltip: Show full UUID on hover (default: True)
342
-
343
- Usage:
344
- html.uuid_short(obj.id) # "a1b2c3..."
345
- html.uuid_short(obj.id, length=8) # "a1b2c3d4..."
346
- html.uuid_short(obj.id, show_tooltip=False) # Just short version
347
-
348
- Returns:
349
- SafeString with shortened UUID
350
- """
351
- uuid_str = str(uuid_value)
352
-
353
- # Remove dashes for cleaner display
354
- uuid_clean = uuid_str.replace('-', '')
355
-
356
- # Take first N characters
357
- short_uuid = uuid_clean[:length]
358
-
359
- if show_tooltip:
360
- return format_html(
361
- '<code class="font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded cursor-help" title="{}">{}</code>',
362
- uuid_str,
363
- short_uuid
364
- )
365
-
366
- return format_html(
367
- '<code class="font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded">{}</code>',
368
- short_uuid
369
- )
370
-
371
- @staticmethod
372
- def markdown_docs(
373
- content: Union[str, Path],
374
- collapsible: bool = True,
375
- title: str = "Documentation",
376
- icon: str = "description",
377
- max_height: Optional[str] = "500px",
378
- enable_plugins: bool = True,
379
- default_open: bool = False
380
- ) -> SafeString:
381
- """
382
- Render markdown documentation from string or file with collapsible UI.
383
-
384
- Auto-detects whether content is a file path or markdown string.
385
-
386
- Args:
387
- content: Markdown string or path to .md file
388
- collapsible: Wrap in collapsible details/summary
389
- title: Title for collapsible section
390
- icon: Material icon name for title
391
- max_height: Max height for scrolling
392
- enable_plugins: Enable markdown plugins
393
- default_open: Open by default if collapsible
394
-
395
- Usage:
396
- # From string with collapse
397
- html.markdown_docs(obj.description, title="Description")
398
-
399
- # From file
400
- html.markdown_docs("docs/api.md", title="API Documentation")
401
-
402
- # Simple, no collapse
403
- html.markdown_docs(obj.notes, collapsible=False)
404
-
405
- # Open by default
406
- html.markdown_docs(obj.readme, default_open=True)
407
-
408
- Returns:
409
- Rendered markdown with beautiful Tailwind styling
410
- """
411
- return MarkdownRenderer.render(
412
- content=content,
413
- collapsible=collapsible,
414
- title=title,
415
- icon=icon,
416
- max_height=max_height,
417
- enable_plugins=enable_plugins,
418
- default_open=default_open
419
- )
30
+ # === BaseElements ===
31
+ icon = staticmethod(BaseElements.icon)
32
+ span = staticmethod(BaseElements.span)
33
+ text = staticmethod(BaseElements.text)
34
+ div = staticmethod(BaseElements.div)
35
+ link = staticmethod(BaseElements.link)
36
+ empty = staticmethod(BaseElements.empty)
37
+
38
+ # === CodeElements ===
39
+ code = staticmethod(CodeElements.code)
40
+ code_block = staticmethod(CodeElements.code_block)
41
+
42
+ # === BadgeElements ===
43
+ badge = staticmethod(BadgeElements.badge)
44
+
45
+ # === CompositionElements ===
46
+ inline = staticmethod(CompositionElements.inline)
47
+ icon_text = staticmethod(CompositionElements.icon_text)
48
+ header = staticmethod(CompositionElements.header)
49
+
50
+ # === FormattingElements ===
51
+ number = staticmethod(FormattingElements.number)
52
+ uuid_short = staticmethod(FormattingElements.uuid_short)
53
+
54
+ # === KeyValueElements ===
55
+ key_value = staticmethod(KeyValueElements.key_value)
56
+ breakdown = staticmethod(KeyValueElements.breakdown)
57
+ divider = staticmethod(KeyValueElements.divider)
58
+ key_value_list = staticmethod(KeyValueElements.key_value_list)
59
+
60
+ # === ProgressElements ===
61
+ segment = staticmethod(ProgressElements.segment)
62
+ progress_bar = staticmethod(ProgressElements.progress_bar)
63
+
64
+ # === MarkdownIntegration ===
65
+ markdown = staticmethod(MarkdownIntegration.markdown)
66
+ markdown_docs = staticmethod(MarkdownIntegration.markdown_docs)
@@ -0,0 +1,21 @@
1
+ """
2
+ Markdown rendering utilities for Django Admin.
3
+
4
+ Provides MarkdownRenderer and mermaid plugin support.
5
+ """
6
+
7
+ from .mermaid_plugin import (
8
+ get_mermaid_resources,
9
+ get_mermaid_script,
10
+ get_mermaid_styles,
11
+ mermaid_plugin,
12
+ )
13
+ from .renderer import MarkdownRenderer
14
+
15
+ __all__ = [
16
+ "MarkdownRenderer",
17
+ "mermaid_plugin",
18
+ "get_mermaid_styles",
19
+ "get_mermaid_script",
20
+ "get_mermaid_resources",
21
+ ]
@@ -140,36 +140,39 @@ class NavigationManager(BaseCfgModule):
140
140
  separator=True,
141
141
  collapsible=True,
142
142
  items=[
143
- NavigationItem(title="Dashboard", icon=Icons.MONITOR_HEART, link="/cfg/admin/admin/centrifugo"),
143
+ NavigationItem(title="Dashboard", icon=Icons.MONITOR_HEART, link="/cfg/admin/admin/dashboard/centrifugo"),
144
144
  NavigationItem(title="Logs", icon=Icons.LIST_ALT, link=str(reverse_lazy("admin:django_cfg_centrifugo_centrifugolog_changelist"))),
145
145
  ]
146
146
  )
147
147
  )
148
148
 
149
- # Background Tasks section (if enabled)
150
- if self.should_enable_tasks():
151
- tasks_items = []
149
+ # gRPC Dashboard (if enabled)
150
+ if self.is_grpc_enabled():
151
+ grpc_items = []
152
152
 
153
- tasks_items.append(
154
- NavigationItem(title="Dashboard", icon=Icons.SETTINGS_APPLICATIONS, link="/cfg/admin/admin/tasks")
153
+ # Monitoring API endpoint
154
+ grpc_items.append(
155
+ NavigationItem(title="Monitor", icon=Icons.MONITOR_HEART, link="/cfg/grpc/monitor/overview/")
155
156
  )
156
157
 
157
- # Try to add Tasks Logs with safe URL resolution
158
+ # Request Logs with safe URL resolution
158
159
  logs_item = self._create_nav_item(
159
- title="Logs",
160
- icon=Icons.TASK,
161
- url_name="admin:django_cfg_tasks_tasklog_changelist",
162
- fallback_link="/admin/django_cfg_tasks/tasklog/"
160
+ title="Request Logs",
161
+ icon=Icons.LIST_ALT,
162
+ url_name="admin:grpc_grpcrequestlog_changelist",
163
+ fallback_link="/admin/grpc/grpcrequestlog/"
163
164
  )
164
165
  if logs_item:
165
- tasks_items.append(logs_item)
166
+ grpc_items.append(logs_item)
166
167
 
167
- navigation_sections.append(NavigationSection(
168
- title="Background Tasks",
169
- separator=True,
170
- collapsible=True,
171
- items=tasks_items
172
- ))
168
+ navigation_sections.append(
169
+ NavigationSection(
170
+ title="gRPC",
171
+ separator=True,
172
+ collapsible=True,
173
+ items=grpc_items
174
+ )
175
+ )
173
176
 
174
177
  # System Operations section
175
178
  system_items = []
django_cfg/pyproject.toml CHANGED
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.4.120"
7
+ version = "1.5.2"
8
8
  description = "Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "nextjs-admin", "react-admin", "websocket", "centrifugo", "real-time", "typescript-generation", "ai-agents", "enterprise-django", "django-settings", "type-safe-config", "modern-django",]
11
11
  classifiers = [ "Development Status :: 4 - Beta", "Framework :: Django", "Framework :: Django :: 5.2", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Systems Administration", "Typing :: Typed",]
12
12
  requires-python = ">=3.12,<3.14"
13
- dependencies = [ "pydantic>=2.11.0,<3.0", "pydantic[email]>=2.11.0,<3.0", "PyYAML>=6.0,<7.0", "click>=8.2.0,<9.0", "questionary>=2.1.0,<3.0", "rich>=14.0.0,<15.0", "cloudflare>=4.3.0,<5.0", "loguru>=0.7.0,<1.0", "colorlog>=6.9.0,<7.0", "cachetools>=5.3.0,<7.0", "toml>=0.10.2,<0.11.0", "ngrok>=1.5.1; python_version>='3.12'", "psycopg[binary,pool]>=3.2.0,<4.0", "dj-database-url>=3.0.0,<4.0", "whitenoise>=6.8.0,<7.0", "django-cors-headers>=4.7.0,<5.0", "django-q2==1.8.0", "croniter>=6.0.0,<7.0", "djangorestframework>=3.16.0,<4.0", "djangorestframework-simplejwt>=5.5.0,<6.0", "djangorestframework-simplejwt[token-blacklist]>=5.5.0,<6.0", "drf-nested-routers>=0.94.0,<1.0", "django-filter>=25.0,<26.0", "django-ratelimit>=4.1.0,<5.0.0", "drf-spectacular>=0.28.0,<1.0", "drf-spectacular-sidecar>=2025.8.0,<2026.0", "django-json-widget>=2.0.0,<3.0", "django-import-export>=4.3.0,<5.0", "django-extensions>=4.1.0,<5.0", "django-constance>=4.3.0,<5.0", "django-unfold>=0.64.0,<1.0", "django-redis>=6.0.0,<7.0", "redis>=6.4.0,<7.0", "hiredis>=2.0.0,<4.0", "rearq>=0.2.0,<1.0", "setuptools>=75.0.0; python_version>='3.13'", "pyTelegramBotAPI>=4.28.0,<5.0", "coolname>=2.2.0,<3.0", "django-admin-rangefilter>=0.13.0,<1.0", "python-json-logger>=3.3.0,<4.0", "requests>=2.32.0,<3.0", "tiktoken>=0.11.0,<1.0", "openai>=1.107.0,<2.0", "twilio>=9.8.0,<10.0", "sendgrid>=6.12.0,<7.0", "beautifulsoup4>=4.13.0,<5.0", "lxml>=6.0.0,<7.0", "pgvector>=0.4.0,<1.0", "tenacity>=9.1.2,<10.0.0", "mypy (>=1.18.2,<2.0.0)", "django-tailwind[reload] (>=4.2.0,<5.0.0)", "jinja2 (>=3.1.6,<4.0.0)", "django-axes[ipware] (>=8.0.0,<9.0.0)", "pydantic-settings (>=2.11.0,<3.0.0)", "httpx>=0.28.1,<1.0", "mistune>=3.1.4,<4.0",]
13
+ dependencies = [ "pydantic>=2.11.0,<3.0", "pydantic[email]>=2.11.0,<3.0", "PyYAML>=6.0,<7.0", "click>=8.2.0,<9.0", "questionary>=2.1.0,<3.0", "rich>=14.0.0,<15.0", "cloudflare>=4.3.0,<5.0", "loguru>=0.7.0,<1.0", "colorlog>=6.9.0,<7.0", "cachetools>=5.3.0,<7.0", "toml>=0.10.2,<0.11.0", "ngrok>=1.5.1; python_version>='3.12'", "psycopg[binary,pool]>=3.2.0,<4.0", "dj-database-url>=3.0.0,<4.0", "whitenoise>=6.8.0,<7.0", "django-cors-headers>=4.7.0,<5.0", "django-rq>=3.1.0,<4.0", "rq-scheduler>=0.14.0,<1.0", "djangorestframework>=3.16.0,<4.0", "djangorestframework-simplejwt>=5.5.0,<6.0", "djangorestframework-simplejwt[token-blacklist]>=5.5.0,<6.0", "drf-nested-routers>=0.94.0,<1.0", "django-filter>=25.0,<26.0", "django-ratelimit>=4.1.0,<5.0.0", "drf-spectacular>=0.28.0,<1.0", "drf-spectacular-sidecar>=2025.8.0,<2026.0", "django-json-widget>=2.0.0,<3.0", "django-import-export>=4.3.0,<5.0", "django-extensions>=4.1.0,<5.0", "django-constance>=4.3.0,<5.0", "django-unfold>=0.64.0,<1.0", "django-redis>=6.0.0,<7.0", "redis>=6.4.0,<7.0", "hiredis>=2.0.0,<4.0", "setuptools>=75.0.0; python_version>='3.13'", "pyTelegramBotAPI>=4.28.0,<5.0", "coolname>=2.2.0,<3.0", "django-admin-rangefilter>=0.13.0,<1.0", "python-json-logger>=3.3.0,<4.0", "requests>=2.32.0,<3.0", "tiktoken>=0.11.0,<1.0", "openai>=1.107.0,<2.0", "twilio>=9.8.0,<10.0", "sendgrid>=6.12.0,<7.0", "beautifulsoup4>=4.13.0,<5.0", "lxml>=6.0.0,<7.0", "pgvector>=0.4.0,<1.0", "tenacity>=9.1.2,<10.0.0", "mypy (>=1.18.2,<2.0.0)", "django-tailwind[reload] (>=4.2.0,<5.0.0)", "jinja2 (>=3.1.6,<4.0.0)", "django-axes[ipware] (>=8.0.0,<9.0.0)", "pydantic-settings (>=2.11.0,<3.0.0)", "httpx>=0.28.1,<1.0", "mistune>=3.1.4,<4.0",]
14
14
  [[project.authors]]
15
15
  name = "Django-CFG Team"
16
16
  email = "info@djangocfg.com"
@@ -26,11 +26,12 @@ text = "MIT"
26
26
  local = []
27
27
  django52 = [ "django>=5.2,<6.0",]
28
28
  ai = [ "pydantic-ai>=1.0.10,<2.0",]
29
+ grpc = [ "grpcio>=1.50.0,<2.0", "grpcio-tools>=1.50.0,<2.0", "protobuf>=5.0,<7.0", "djangogrpcframework>=0.2.1",]
29
30
  dev = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "factory-boy>=3.3,<4.0", "fakeredis>=2.28,<3.0", "black>=25.9,<26.0", "isort>=6.0,<7.0", "flake8>=6.0.0,<8.0", "mypy>=1.18,<2.0", "pre-commit>=4.3,<5.0", "build>=1.3,<2.0", "twine>=6.2,<7.0", "tomlkit>=0.13.3,<1.0", "questionary>=2.1.0,<3.0", "rich>=13.0.0,<15.0", "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "redis>=6.4.0,<7.0",]
30
31
  test = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "pytest-xdist>=3.8,<4.0", "factory-boy>=3.3,<4.0", "fakeredis>=2.28,<3.0",]
31
32
  docs = [ "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "pymdown-extensions>=10.16,<11.0",]
32
33
  tasks = [ "redis>=6.4.0,<7.0",]
33
- full = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "pytest-xdist>=3.8,<4.0", "factory-boy>=3.3,<4.0", "black>=25.9,<26.0", "isort>=6.0,<7.0", "flake8>=6.0.0,<8.0", "mypy>=1.18,<2.0", "pre-commit>=4.3,<5.0", "build>=1.3,<2.0", "twine>=6.2,<7.0", "tomlkit>=0.13.3,<1.0", "questionary>=2.1.0,<3.0", "rich>=13.0.0,<15.0", "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "pymdown-extensions>=10.16,<11.0", "redis>=6.4.0,<7.0",]
34
+ full = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "pytest-xdist>=3.8,<4.0", "factory-boy>=3.3,<4.0", "black>=25.9,<26.0", "isort>=6.0,<7.0", "flake8>=6.0.0,<8.0", "mypy>=1.18,<2.0", "pre-commit>=4.3,<5.0", "build>=1.3,<2.0", "twine>=6.2,<7.0", "tomlkit>=0.13.3,<1.0", "questionary>=2.1.0,<3.0", "rich>=13.0.0,<15.0", "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "pymdown-extensions>=10.16,<11.0", "redis>=6.4.0,<7.0", "grpcio>=1.50.0,<2.0", "grpcio-tools>=1.50.0,<2.0", "protobuf>=5.0,<7.0", "djangogrpcframework>=0.2.1",]
34
35
 
35
36
  [project.urls]
36
37
  Homepage = "https://djangocfg.com"
@@ -114,9 +115,6 @@ exclude_lines = [ "pragma: no cover", "def __repr__", "if self.debug:", "if sett
114
115
  [tool.coverage.html]
115
116
  directory = "htmlcov"
116
117
 
117
- [tool.poetry.group.local]
118
- optional = true
119
-
120
118
  [tool.poetry.group.dev.dependencies]
121
119
  tomlkit = "^0.13.3"
122
120
  build = "^1.3.0"
@@ -38,10 +38,6 @@ CORE_REGISTRY = {
38
38
  # Security - Django Crypto Fields
39
39
  "CryptoFieldsConfig": ("django_cfg.models.django.crypto_fields", "CryptoFieldsConfig"),
40
40
 
41
- # Scheduling - Django-Q2
42
- "DjangoQ2Config": ("django_cfg.models.django.django_q2", "DjangoQ2Config"),
43
- "DjangoQ2ScheduleConfig": ("django_cfg.models.django.django_q2", "DjangoQ2ScheduleConfig"),
44
-
45
41
  # Limits models
46
42
  "LimitsConfig": ("django_cfg.models.api.limits", "LimitsConfig"),
47
43
 
@@ -51,9 +47,10 @@ CORE_REGISTRY = {
51
47
  # JWT models
52
48
  "JWTConfig": ("django_cfg.models.api.jwt", "JWTConfig"),
53
49
 
54
- # Task and queue models
55
- "TaskConfig": ("django_cfg.models.tasks.config", "TaskConfig"),
56
- "RearqConfig": ("django_cfg.models.tasks.config", "RearqConfig"),
50
+ # Django-RQ models
51
+ "DjangoRQConfig": ("django_cfg.models.django.django_rq", "DjangoRQConfig"),
52
+ "RQQueueConfig": ("django_cfg.models.django.django_rq", "RQQueueConfig"),
53
+ "RQScheduleConfig": ("django_cfg.models.django.django_rq", "RQScheduleConfig"),
57
54
 
58
55
  # Payment system models (BaseCfgAutoModule)
59
56
  "PaymentsConfig": ("django_cfg.models.payments.config", "PaymentsConfig"),
@@ -13,6 +13,12 @@ MODULES_REGISTRY = {
13
13
  # Centrifugo module
14
14
  "DjangoCfgCentrifugoConfig": ("django_cfg.apps.centrifugo.services.client.config", "DjangoCfgCentrifugoConfig"),
15
15
 
16
+ # gRPC module
17
+ "GRPCConfig": ("django_cfg.models.api.grpc", "GRPCConfig"),
18
+ "GRPCServerConfig": ("django_cfg.models.api.grpc", "GRPCServerConfig"),
19
+ "GRPCAuthConfig": ("django_cfg.models.api.grpc", "GRPCAuthConfig"),
20
+ "GRPCProtoConfig": ("django_cfg.models.api.grpc", "GRPCProtoConfig"),
21
+
16
22
  # Next.js Admin Integration
17
23
  "NextJsAdminConfig": ("django_cfg.modules.nextjs_admin", "NextJsAdminConfig"),
18
24
 
Binary file