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,396 +0,0 @@
1
- # Code Block Methods Documentation
2
-
3
- New methods added to `HtmlBuilder` for displaying code in Django Admin.
4
-
5
- ## Methods Added
6
-
7
- ### 1. `code()` - Inline Code
8
-
9
- Display short code snippets inline.
10
-
11
- **Signature:**
12
- ```python
13
- def code(text: Any, css_class: str = "") -> SafeString
14
- ```
15
-
16
- **Parameters:**
17
- - `text`: Code text to display
18
- - `css_class`: Additional CSS classes (optional)
19
-
20
- **Usage:**
21
- ```python
22
- from django_cfg.modules.django_admin.base import PydanticAdmin
23
-
24
- class MyAdmin(PydanticAdmin):
25
- @computed_field("Command")
26
- def command_display(self, obj):
27
- return self.html.code(obj.full_command)
28
- ```
29
-
30
- **Examples:**
31
- ```python
32
- # File path
33
- self.html.code("/path/to/file")
34
-
35
- # Command
36
- self.html.code("python manage.py migrate")
37
-
38
- # Configuration value
39
- self.html.code("DEBUG=True")
40
- ```
41
-
42
- **Renders as:**
43
- ```html
44
- <code class="font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded">
45
- /path/to/file
46
- </code>
47
- ```
48
-
49
- ---
50
-
51
- ### 2. `code_block()` - Multi-line Code Block
52
-
53
- Display multi-line code with syntax highlighting hints, scrolling, and color variants.
54
-
55
- **Signature:**
56
- ```python
57
- def code_block(
58
- text: Any,
59
- language: Optional[str] = None,
60
- max_height: Optional[str] = None,
61
- variant: str = "default"
62
- ) -> SafeString
63
- ```
64
-
65
- **Parameters:**
66
- - `text`: Code content (multi-line supported)
67
- - `language`: Programming language hint (`"json"`, `"python"`, `"bash"`, etc.) - for future syntax highlighting
68
- - `max_height`: Maximum height with overflow scroll (e.g., `"400px"`, `"20rem"`)
69
- - `variant`: Color variant - `"default"`, `"warning"`, `"danger"`, `"success"`, `"info"`
70
-
71
- **Usage:**
72
- ```python
73
- from django_cfg.modules.django_admin.base import PydanticAdmin
74
- import json
75
-
76
- class MyAdmin(PydanticAdmin):
77
- @computed_field("Configuration")
78
- def config_display(self, obj):
79
- if not obj.config:
80
- return self.html.empty()
81
-
82
- return self.html.code_block(
83
- json.dumps(obj.config, indent=2),
84
- language="json"
85
- )
86
-
87
- @computed_field("Standard Output")
88
- def stdout_display(self, obj):
89
- if not obj.stdout:
90
- return self.html.empty()
91
-
92
- return self.html.code_block(
93
- obj.stdout,
94
- max_height="400px"
95
- )
96
-
97
- @computed_field("Error Message")
98
- def error_display(self, obj):
99
- if not obj.error_message:
100
- return self.html.empty()
101
-
102
- return self.html.code_block(
103
- obj.error_message,
104
- max_height="200px",
105
- variant="danger"
106
- )
107
- ```
108
-
109
- **Examples:**
110
-
111
- #### JSON Display
112
- ```python
113
- # Display JSON configuration
114
- self.html.code_block(
115
- json.dumps({"key": "value", "nested": {"foo": "bar"}}, indent=2),
116
- language="json"
117
- )
118
- ```
119
-
120
- #### Log Output with Scrolling
121
- ```python
122
- # Display command output with scroll
123
- self.html.code_block(
124
- "Line 1\nLine 2\nLine 3\n...",
125
- max_height="400px"
126
- )
127
- ```
128
-
129
- #### Error Messages
130
- ```python
131
- # Display error with danger styling
132
- self.html.code_block(
133
- "Traceback (most recent call last):\n File ...",
134
- variant="danger",
135
- max_height="300px"
136
- )
137
- ```
138
-
139
- #### Warnings
140
- ```python
141
- # Display warnings with warning styling
142
- self.html.code_block(
143
- "Warning: This is deprecated\nUse new_method() instead",
144
- variant="warning"
145
- )
146
- ```
147
-
148
- **Renders as:**
149
- ```html
150
- <!-- Default variant -->
151
- <pre class="font-mono text-xs whitespace-pre-wrap break-words border rounded-md p-3
152
- bg-base-50 dark:bg-base-900 border-base-200 dark:border-base-700"
153
- style="max-height: 400px; overflow-y: auto;">
154
- <code>{"key": "value"}</code>
155
- </pre>
156
-
157
- <!-- Danger variant -->
158
- <pre class="font-mono text-xs whitespace-pre-wrap break-words border rounded-md p-3
159
- bg-danger-50 dark:bg-danger-900/20 border-danger-200 dark:border-danger-700"
160
- style="max-height: 200px; overflow-y: auto;">
161
- <code>Error: Something went wrong</code>
162
- </pre>
163
- ```
164
-
165
- ---
166
-
167
- ## Color Variants
168
-
169
- | Variant | Background | Border | Use Case |
170
- |---------|-----------|--------|----------|
171
- | `default` | Gray | Gray | Standard code, JSON, logs |
172
- | `warning` | Yellow/Orange | Yellow/Orange | Warnings, stderr output |
173
- | `danger` | Red | Red | Errors, exceptions, failures |
174
- | `success` | Green | Green | Success messages, confirmations |
175
- | `info` | Blue | Blue | Info messages, metadata |
176
-
177
- ---
178
-
179
- ## Use Cases
180
-
181
- ### 1. Command Execution Logs
182
- ```python
183
- @computed_field("Output")
184
- def output_display(self, obj):
185
- return self.html.code_block(
186
- obj.stdout,
187
- max_height="400px"
188
- )
189
- ```
190
-
191
- ### 2. JSON Configuration
192
- ```python
193
- @computed_field("Settings")
194
- def settings_display(self, obj):
195
- return self.html.code_block(
196
- json.dumps(obj.settings, indent=2),
197
- language="json"
198
- )
199
- ```
200
-
201
- ### 3. Error Details
202
- ```python
203
- @computed_field("Error")
204
- def error_display(self, obj):
205
- if not obj.error_message:
206
- return self.html.empty()
207
-
208
- return self.html.code_block(
209
- obj.error_message,
210
- max_height="300px",
211
- variant="danger"
212
- )
213
- ```
214
-
215
- ### 4. API Request/Response
216
- ```python
217
- @computed_field("Request Body")
218
- def request_body_display(self, obj):
219
- return self.html.code_block(
220
- obj.request_body,
221
- language="json",
222
- max_height="300px"
223
- )
224
- ```
225
-
226
- ### 5. SQL Queries
227
- ```python
228
- @computed_field("Query")
229
- def query_display(self, obj):
230
- return self.html.code_block(
231
- obj.sql_query,
232
- language="sql",
233
- max_height="200px"
234
- )
235
- ```
236
-
237
- ---
238
-
239
- ## Styling Details
240
-
241
- ### Tailwind Classes Used
242
-
243
- **Base Classes:**
244
- - `font-mono` - Monospace font
245
- - `text-xs` - Small text size
246
- - `whitespace-pre-wrap` - Preserve whitespace, wrap long lines
247
- - `break-words` - Break long words
248
- - `border` - Border
249
- - `rounded-md` - Rounded corners
250
- - `p-3` - Padding
251
-
252
- **Variant-specific:**
253
- - Light mode: `bg-{variant}-50 border-{variant}-200`
254
- - Dark mode: `dark:bg-{variant}-900/20 dark:border-{variant}-700`
255
-
256
- **Scrolling:**
257
- - Applied via inline `style` attribute
258
- - `max-height` and `overflow-y: auto`
259
-
260
- ---
261
-
262
- ## Implementation Notes
263
-
264
- 1. **Escaping**: All text is automatically escaped using Django's `escape()` to prevent XSS
265
- 2. **SafeString**: Returns Django's `SafeString` for safe HTML rendering
266
- 3. **Dark Mode**: Full dark mode support with Tailwind's dark: prefix
267
- 4. **Responsive**: Works on mobile with `break-words` and `whitespace-pre-wrap`
268
- 5. **Future-proof**: `language` parameter allows for syntax highlighting integration later
269
-
270
- ---
271
-
272
- ## Complete Example: Command Execution Admin
273
-
274
- ```python
275
- from django.contrib import admin
276
- from django_cfg.modules.django_admin import (
277
- AdminConfig, BadgeField, DateTimeField, Icons, computed_field
278
- )
279
- from django_cfg.modules.django_admin.base import PydanticAdmin
280
- import json
281
-
282
- commandexecution_config = AdminConfig(
283
- model=CommandExecution,
284
- list_display=["id", "command", "status", "created_at"],
285
- readonly_fields=[
286
- "command_display",
287
- "args_display",
288
- "stdout_display",
289
- "stderr_display",
290
- "error_display",
291
- ],
292
- )
293
-
294
- @admin.register(CommandExecution)
295
- class CommandExecutionAdmin(PydanticAdmin):
296
- config = commandexecution_config
297
-
298
- @computed_field("Command")
299
- def command_display(self, obj):
300
- """Inline code for command."""
301
- return self.html.code(obj.full_command)
302
-
303
- @computed_field("Arguments")
304
- def args_display(self, obj):
305
- """JSON code block for args."""
306
- if not obj.args:
307
- return self.html.empty()
308
-
309
- return self.html.code_block(
310
- json.dumps(obj.args, indent=2),
311
- language="json"
312
- )
313
-
314
- @computed_field("Standard Output")
315
- def stdout_display(self, obj):
316
- """Scrollable output."""
317
- if not obj.stdout:
318
- return self.html.empty()
319
-
320
- return self.html.code_block(
321
- obj.stdout,
322
- max_height="400px"
323
- )
324
-
325
- @computed_field("Standard Error")
326
- def stderr_display(self, obj):
327
- """Warning-styled error output."""
328
- if not obj.stderr:
329
- return self.html.empty()
330
-
331
- return self.html.code_block(
332
- obj.stderr,
333
- max_height="400px",
334
- variant="warning"
335
- )
336
-
337
- @computed_field("Error Message")
338
- def error_display(self, obj):
339
- """Danger-styled error message."""
340
- if not obj.error_message:
341
- return self.html.empty()
342
-
343
- return self.html.code_block(
344
- obj.error_message,
345
- max_height="200px",
346
- variant="danger"
347
- )
348
- ```
349
-
350
- ---
351
-
352
- ## Testing
353
-
354
- ```python
355
- # Test inline code
356
- assert 'font-mono' in str(html.code("test"))
357
-
358
- # Test code block
359
- assert '<pre' in str(html.code_block("test"))
360
- assert 'overflow-y: auto' in str(html.code_block("test", max_height="400px"))
361
-
362
- # Test variants
363
- assert 'bg-danger' in str(html.code_block("error", variant="danger"))
364
- assert 'bg-warning' in str(html.code_block("warn", variant="warning"))
365
- ```
366
-
367
- ---
368
-
369
- ## Migration from Old Format
370
-
371
- **Before (manual HTML):**
372
- ```python
373
- def output_display(self, obj):
374
- return format_html(
375
- '<pre style="max-height: 400px; overflow-y: auto; '
376
- 'background: #f8f9fa; padding: 10px; border-radius: 4px;">{}</pre>',
377
- obj.stdout
378
- )
379
- ```
380
-
381
- **After (with code_block):**
382
- ```python
383
- def output_display(self, obj):
384
- return self.html.code_block(
385
- obj.stdout,
386
- max_height="400px"
387
- )
388
- ```
389
-
390
- Benefits:
391
- - ✅ Cleaner, more readable code
392
- - ✅ Consistent styling across project
393
- - ✅ Dark mode support
394
- - ✅ Proper escaping
395
- - ✅ Tailwind CSS classes
396
- - ✅ Variant support for different contexts
@@ -1,140 +0,0 @@
1
- # Django-Q2 Module
2
-
3
- Автоматическая синхронизация расписаний Django-Q2 из конфига в базу данных.
4
-
5
- ## Зачем это нужно?
6
-
7
- Django-Q2 хранит расписания в базе данных, но **не создаёт их автоматически** из конфига.
8
- Этот модуль решает эту проблему - синхронизирует расписания после каждой миграции.
9
-
10
- ## Использование
11
-
12
- ### 1. Включи Django-Q2 в конфиге
13
-
14
- ```python
15
- # config.py
16
- from django_cfg.models.django import DjangoQ2Config
17
-
18
- django_q2 = DjangoQ2Config(
19
- enabled=True, # ← Автоматически добавит django_q и django_cfg.modules.django_q2 в INSTALLED_APPS
20
- schedules=[...]
21
- )
22
- ```
23
-
24
- **Модуль подключается автоматически!** Не нужно вручную добавлять в INSTALLED_APPS.
25
-
26
- ### 2. Определи расписания в конфиге
27
-
28
- ```python
29
- from django_cfg.models.django import DjangoQ2Config, DjangoQ2ScheduleConfig
30
-
31
- django_q2 = DjangoQ2Config(
32
- enabled=True,
33
- schedules=[
34
- DjangoQ2ScheduleConfig(
35
- name="Sync balances hourly",
36
- schedule_type="hourly",
37
- command="sync_account_balances",
38
- command_args=["--verbose"],
39
- ),
40
- DjangoQ2ScheduleConfig(
41
- name="Cleanup daily",
42
- schedule_type="cron",
43
- cron="0 2 * * *", # 2 AM каждый день
44
- command="cleanup_old_data",
45
- command_kwargs={"days": 30},
46
- ),
47
- ],
48
- )
49
- ```
50
-
51
- ### 3. Запусти миграции
52
-
53
- ```bash
54
- python manage.py migrate
55
- ```
56
-
57
- **Вывод:**
58
- ```
59
- Running migrations:
60
- ...
61
- Syncing 2 Django-Q2 schedule(s)...
62
- ✓ Created schedule: Sync balances hourly
63
- ✓ Created schedule: Cleanup daily
64
- ✅ Django-Q2 schedules synced: 2 created, 0 updated
65
- ```
66
-
67
- ### 4. Запусти qcluster
68
-
69
- ```bash
70
- python manage.py qcluster
71
- ```
72
-
73
- Готово! Расписания автоматически синхронизированы и работают.
74
-
75
- ## Как это работает?
76
-
77
- 1. **Модуль подключается** к сигналу `post_migrate`
78
- 2. **После миграций** автоматически:
79
- - Читает расписания из конфига
80
- - Создаёт/обновляет их в базе данных (Schedule model)
81
- 3. **Django-Q2 читает** расписания из базы и выполняет задачи
82
-
83
- ## Ручная синхронизация (опционально)
84
-
85
- Если нужно синхронизировать без миграций:
86
-
87
- ```bash
88
- python manage.py sync_django_q_schedules
89
-
90
- # Или с --dry-run для проверки:
91
- python manage.py sync_django_q_schedules --dry-run
92
- ```
93
-
94
- ## Безопасность
95
-
96
- - ✅ **Идемпотентность**: можно запускать много раз, не создаст дубликаты
97
- - ✅ **Без race conditions**: синхронизация происходит один раз за цикл миграций
98
- - ✅ **Graceful degradation**: если Django-Q2 не установлен, модуль просто молча пропустит синхронизацию
99
- - ✅ **Logging**: все операции логируются для отладки
100
-
101
- ## Преимущества перед ручной синхронизацией
102
-
103
- | Аспект | Ручная синхронизация | Модуль |
104
- |--------|---------------------|--------|
105
- | Автоматизация | Нужно помнить запускать | Автоматически |
106
- | Деплой | Легко забыть | Всегда синхронизировано |
107
- | CI/CD | Нужно добавлять в скрипты | Работает из коробки |
108
- | Ошибки | Легко пропустить | Логи миграций |
109
-
110
- ## Troubleshooting
111
-
112
- ### Расписания не создаются
113
-
114
- Проверь:
115
- 1. Модуль добавлен в INSTALLED_APPS
116
- 2. `django_q2.enabled = True` в конфиге
117
- 3. В конфиге есть расписания
118
- 4. Миграции запущены: `python manage.py migrate`
119
-
120
- ### Расписания не обновляются
121
-
122
- Запусти миграции повторно или используй ручную синхронизацию:
123
- ```bash
124
- python manage.py migrate --run-syncdb
125
- # или
126
- python manage.py sync_django_q_schedules
127
- ```
128
-
129
- ### Логи синхронизации
130
-
131
- Включи DEBUG логи:
132
- ```python
133
- LOGGING = {
134
- 'loggers': {
135
- 'django_cfg.modules.django_q2': {
136
- 'level': 'DEBUG',
137
- },
138
- },
139
- }
140
- ```
@@ -1,8 +0,0 @@
1
- """
2
- Django-Q2 module for django-cfg.
3
-
4
- Provides automatic schedule synchronization from config to database.
5
- """
6
- from .apps import DjangoQ2ModuleConfig
7
-
8
- __all__ = ['DjangoQ2ModuleConfig']
@@ -1,107 +0,0 @@
1
- """
2
- AppConfig for Django-Q2 module with automatic schedule synchronization.
3
- """
4
- import logging
5
- from django.apps import AppConfig
6
- from django.db.models.signals import post_migrate
7
-
8
- logger = logging.getLogger(__name__)
9
-
10
-
11
- def sync_schedules_after_migrate(sender, **kwargs):
12
- """
13
- Automatically sync Django-Q2 schedules after migrations.
14
-
15
- This ensures schedules are always up-to-date after deployment.
16
- Runs only once per migration cycle, safe from race conditions.
17
- """
18
- # Only run for the django_cfg_django_q2 app itself
19
- if sender.name != 'django_cfg.modules.django_q2':
20
- return
21
-
22
- # Import here to avoid circular imports and ensure Django is ready
23
- try:
24
- from django_q.models import Schedule
25
- from django_cfg.core.config import get_current_config
26
- except ImportError as e:
27
- logger.warning(f"Could not import Django-Q2 dependencies: {e}")
28
- return
29
-
30
- config = get_current_config()
31
-
32
- if not config or not hasattr(config, 'django_q2') or not config.django_q2 or not config.django_q2.enabled:
33
- logger.debug("Django-Q2 not enabled, skipping schedule sync")
34
- return
35
-
36
- enabled_schedules = config.django_q2.get_enabled_schedules()
37
-
38
- if not enabled_schedules:
39
- logger.debug("No Django-Q2 schedules found in config")
40
- return
41
-
42
- logger.info(f"Syncing {len(enabled_schedules)} Django-Q2 schedule(s)...")
43
-
44
- created = 0
45
- updated = 0
46
-
47
- for schedule_config in enabled_schedules:
48
- schedule_dict = schedule_config.to_django_q_format()
49
- name = schedule_dict['name']
50
-
51
- try:
52
- schedule, created_flag = Schedule.objects.update_or_create(
53
- name=name,
54
- defaults=schedule_dict
55
- )
56
-
57
- if created_flag:
58
- created += 1
59
- logger.info(f" ✓ Created schedule: {name}")
60
- else:
61
- updated += 1
62
- logger.debug(f" ✓ Updated schedule: {name}")
63
-
64
- except Exception as e:
65
- logger.error(f" ✗ Failed to sync schedule '{name}': {e}")
66
-
67
- logger.info(f"✅ Django-Q2 schedules synced: {created} created, {updated} updated")
68
-
69
-
70
- class DjangoQ2ModuleConfig(AppConfig):
71
- """
72
- AppConfig for Django-Q2 module.
73
-
74
- Automatically syncs schedules from config to database after migrations.
75
- This eliminates the need for manual `sync_django_q_schedules` command.
76
-
77
- Features:
78
- - Automatic schedule sync after migrations
79
- - Safe from race conditions (runs only once)
80
- - Logs all sync operations
81
- - Gracefully handles missing dependencies
82
-
83
- Usage:
84
- Add to INSTALLED_APPS:
85
- INSTALLED_APPS = [
86
- ...
87
- 'django_cfg.modules.django_q2', # Auto-syncs schedules
88
- ]
89
- """
90
-
91
- default_auto_field = 'django.db.models.BigAutoField'
92
- name = 'django_cfg.modules.django_q2'
93
- verbose_name = 'Django-CFG Django-Q2 Module'
94
-
95
- def ready(self):
96
- """
97
- Connect post_migrate signal to automatically sync schedules.
98
-
99
- This runs after all migrations are complete, ensuring:
100
- 1. Database tables exist
101
- 2. Config is loaded
102
- 3. Schedules are synced only once per migration cycle
103
- """
104
- # Connect the signal
105
- post_migrate.connect(sync_schedules_after_migrate, sender=self)
106
-
107
- logger.debug(f"{self.verbose_name} initialized - auto-sync enabled")