django-cfg 1.3.9__py3-none-any.whl → 1.3.13__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 (188) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/inlines.py +11 -5
  3. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  4. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  5. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  14. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  15. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  16. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  17. django_cfg/apps/payments/config/__init__.py +14 -15
  18. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  19. django_cfg/apps/payments/config/helpers.py +8 -13
  20. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  21. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  22. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  23. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  24. django_cfg/apps/payments/models/payments.py +94 -0
  25. django_cfg/apps/payments/services/core/base.py +4 -4
  26. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  27. django_cfg/apps/payments/services/providers/base.py +209 -3
  28. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  29. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  31. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  32. django_cfg/apps/payments/services/providers/registry.py +5 -5
  33. django_cfg/apps/payments/services/types/requests.py +19 -7
  34. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  35. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  36. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  37. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  38. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  39. django_cfg/apps/payments/urls.py +3 -2
  40. django_cfg/apps/payments/views/api/currencies.py +3 -0
  41. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  42. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  43. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  44. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  45. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  47. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  52. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  53. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  54. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  55. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  56. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  57. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  58. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  59. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  60. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  61. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  62. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  63. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  66. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  67. django_cfg/apps/tasks/urls.py +2 -2
  68. django_cfg/apps/tasks/urls_admin.py +2 -2
  69. django_cfg/apps/tasks/utils/__init__.py +1 -0
  70. django_cfg/apps/tasks/utils/simulator.py +356 -0
  71. django_cfg/apps/tasks/views/__init__.py +16 -0
  72. django_cfg/apps/tasks/views/api.py +569 -0
  73. django_cfg/apps/tasks/views/dashboard.py +58 -0
  74. django_cfg/core/integration/__init__.py +21 -0
  75. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  76. django_cfg/models/constance.py +0 -11
  77. django_cfg/models/payments.py +137 -3
  78. django_cfg/modules/django_tasks.py +54 -21
  79. django_cfg/registry/core.py +4 -9
  80. django_cfg/template_archive/django_sample.zip +0 -0
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/METADATA +2 -2
  82. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/RECORD +85 -153
  83. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  84. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  85. django_cfg/apps/payments/config/constance/fields.py +0 -69
  86. django_cfg/apps/payments/config/constance/settings.py +0 -160
  87. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  88. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  89. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  90. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  91. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  92. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  93. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  94. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  95. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  96. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  97. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  98. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  99. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  100. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  101. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  102. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  103. django_cfg/apps/tasks/views.py +0 -461
  104. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  105. django_cfg/management/commands/app_agent_generate.py +0 -342
  106. django_cfg/management/commands/app_agent_info.py +0 -308
  107. django_cfg/management/commands/auto_generate.py +0 -486
  108. django_cfg/modules/django_app_agent/__init__.py +0 -87
  109. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  110. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  111. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  112. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  113. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  114. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  119. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  120. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  121. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  122. django_cfg/modules/django_app_agent/core/config.py +0 -300
  123. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  124. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  125. django_cfg/modules/django_app_agent/models/base.py +0 -283
  126. django_cfg/modules/django_app_agent/models/context.py +0 -496
  127. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  128. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  129. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  130. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  131. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  132. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  133. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  134. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  135. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  136. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  137. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  138. django_cfg/modules/django_app_agent/services/base.py +0 -437
  139. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  140. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  141. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  142. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  143. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  144. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  145. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  146. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  147. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  148. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  149. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  150. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  151. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  152. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  153. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  154. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  155. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  156. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  157. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  158. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  159. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  160. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  161. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  171. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  172. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  173. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  174. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  175. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  176. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  177. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  178. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  179. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  180. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  181. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  182. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  183. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  184. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  185. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/WHEEL +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/entry_points.txt +0 -0
  188. {django_cfg-1.3.9.dist-info → django_cfg-1.3.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,454 +0,0 @@
1
- /**
2
- * Task Monitor Modal
3
- * Interactive task monitoring with real-time updates
4
- */
5
- class TaskMonitor {
6
- constructor() {
7
- this.modal = null;
8
- this.autoRefresh = true;
9
- this.refreshInterval = null;
10
- this.refreshRate = 5000; // 5 seconds
11
- this.currentFilters = {
12
- status: '',
13
- queue: '',
14
- search: ''
15
- };
16
- this.tasks = [];
17
-
18
- this.init();
19
- }
20
-
21
- init() {
22
- this.modal = document.getElementById('task-details-modal');
23
- if (!this.modal) return;
24
-
25
- this.setupEventListeners();
26
- this.startAutoRefresh();
27
- }
28
-
29
- setupEventListeners() {
30
- // Modal controls
31
- document.getElementById('close-task-modal')?.addEventListener('click', () => this.hide());
32
- document.getElementById('refresh-tasks-modal')?.addEventListener('click', () => this.loadTasks());
33
- document.getElementById('auto-refresh-toggle')?.addEventListener('click', () => this.toggleAutoRefresh());
34
-
35
- // Filters
36
- document.getElementById('modal-status-filter')?.addEventListener('change', (e) => {
37
- this.currentFilters.status = e.target.value;
38
- this.applyFilters();
39
- });
40
-
41
- document.getElementById('modal-queue-filter')?.addEventListener('change', (e) => {
42
- this.currentFilters.queue = e.target.value;
43
- this.applyFilters();
44
- });
45
-
46
- document.getElementById('modal-search-input')?.addEventListener('input', (e) => {
47
- this.currentFilters.search = e.target.value.toLowerCase();
48
- this.applyFilters();
49
- });
50
-
51
- // Actions
52
- document.getElementById('clear-completed-tasks')?.addEventListener('click', () => this.clearCompletedTasks());
53
- document.getElementById('export-tasks')?.addEventListener('click', () => this.exportTasks());
54
-
55
- // Close modal on outside click
56
- this.modal.addEventListener('click', (e) => {
57
- if (e.target === this.modal) {
58
- this.hide();
59
- }
60
- });
61
-
62
- // Close modal on Escape key
63
- document.addEventListener('keydown', (e) => {
64
- if (e.key === 'Escape' && !this.modal.classList.contains('hidden')) {
65
- this.hide();
66
- }
67
- });
68
- }
69
-
70
- show() {
71
- if (!this.modal) return;
72
-
73
- this.modal.classList.remove('hidden');
74
- document.body.style.overflow = 'hidden';
75
- this.loadTasks();
76
-
77
- if (this.autoRefresh) {
78
- this.startAutoRefresh();
79
- }
80
- }
81
-
82
- hide() {
83
- if (!this.modal) return;
84
-
85
- this.modal.classList.add('hidden');
86
- document.body.style.overflow = '';
87
- this.stopAutoRefresh();
88
- }
89
-
90
- toggleAutoRefresh() {
91
- this.autoRefresh = !this.autoRefresh;
92
- const button = document.getElementById('auto-refresh-toggle');
93
-
94
- if (this.autoRefresh) {
95
- button.innerHTML = '<span class="material-icons text-sm mr-1">refresh</span>Auto: ON';
96
- button.className = 'px-3 py-1 text-sm bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-lg hover:bg-green-200 dark:hover:bg-green-800 transition-colors';
97
- this.startAutoRefresh();
98
- } else {
99
- button.innerHTML = '<span class="material-icons text-sm mr-1">pause</span>Auto: OFF';
100
- button.className = 'px-3 py-1 text-sm bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors';
101
- this.stopAutoRefresh();
102
- }
103
- }
104
-
105
- startAutoRefresh() {
106
- if (this.refreshInterval) {
107
- clearInterval(this.refreshInterval);
108
- }
109
-
110
- if (this.autoRefresh) {
111
- this.refreshInterval = setInterval(() => {
112
- if (!this.modal.classList.contains('hidden')) {
113
- this.loadTasks();
114
- }
115
- }, this.refreshRate);
116
- }
117
- }
118
-
119
- stopAutoRefresh() {
120
- if (this.refreshInterval) {
121
- clearInterval(this.refreshInterval);
122
- this.refreshInterval = null;
123
- }
124
- }
125
-
126
- async loadTasks() {
127
- try {
128
- this.showLoading();
129
-
130
- // Get real task data from API
131
- const params = {
132
- limit: 100,
133
- offset: 0,
134
- ...this.currentFilters
135
- };
136
-
137
- const response = await window.tasksAPI.getTaskList(params);
138
- const data = response.data || response;
139
-
140
- if (data.error) {
141
- // Fallback to mock data if API fails
142
- console.warn('API error, using mock data:', data.error);
143
- this.tasks = this.generateMockTasks();
144
- } else {
145
- this.tasks = data.tasks || [];
146
- }
147
-
148
- this.updateTaskCount();
149
- this.updateLastUpdateTime();
150
- this.renderTasks();
151
-
152
- } catch (error) {
153
- console.error('Failed to load tasks:', error);
154
- // Fallback to mock data on error
155
- this.tasks = this.generateMockTasks();
156
- this.updateTaskCount();
157
- this.updateLastUpdateTime();
158
- this.renderTasks();
159
- }
160
- }
161
-
162
- generateMockTasks() {
163
- // Generate mock task data - replace with real API data
164
- const statuses = ['pending', 'running', 'done', 'failed'];
165
- const queues = ['default', 'high', 'low', 'vehicles'];
166
- const actors = ['process_document_async', 'send_notification', 'cleanup_old_files', 'generate_report'];
167
-
168
- return Array.from({ length: 20 }, (_, i) => ({
169
- id: `task_${i + 1}`,
170
- actor_name: actors[Math.floor(Math.random() * actors.length)],
171
- status: statuses[Math.floor(Math.random() * statuses.length)],
172
- queue: queues[Math.floor(Math.random() * queues.length)],
173
- created_at: new Date(Date.now() - Math.random() * 86400000).toISOString(),
174
- updated_at: new Date(Date.now() - Math.random() * 3600000).toISOString(),
175
- args: JSON.stringify([Math.floor(Math.random() * 1000)]),
176
- kwargs: JSON.stringify({ user_id: Math.floor(Math.random() * 100) }),
177
- progress: Math.floor(Math.random() * 100),
178
- result: Math.random() > 0.7 ? JSON.stringify({ success: true, processed: Math.floor(Math.random() * 100) }) : null,
179
- traceback: Math.random() > 0.9 ? 'Error: Something went wrong...' : null
180
- }));
181
- }
182
-
183
- applyFilters() {
184
- this.renderTasks();
185
- }
186
-
187
- getFilteredTasks() {
188
- return this.tasks.filter(task => {
189
- const matchesStatus = !this.currentFilters.status || task.status === this.currentFilters.status;
190
- const matchesQueue = !this.currentFilters.queue || task.queue === this.currentFilters.queue;
191
- const matchesSearch = !this.currentFilters.search ||
192
- task.actor_name.toLowerCase().includes(this.currentFilters.search) ||
193
- task.id.toLowerCase().includes(this.currentFilters.search);
194
-
195
- return matchesStatus && matchesQueue && matchesSearch;
196
- });
197
- }
198
-
199
- renderTasks() {
200
- const container = document.getElementById('tasks-list');
201
- const loading = document.getElementById('tasks-loading');
202
- const empty = document.getElementById('tasks-empty');
203
-
204
- if (!container) return;
205
-
206
- loading.classList.add('hidden');
207
-
208
- const filteredTasks = this.getFilteredTasks();
209
-
210
- if (filteredTasks.length === 0) {
211
- container.innerHTML = '';
212
- empty.classList.remove('hidden');
213
- return;
214
- }
215
-
216
- empty.classList.add('hidden');
217
-
218
- // Create table layout
219
- container.innerHTML = `
220
- <div class="overflow-x-auto">
221
- <table class="min-w-full bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
222
- <thead class="bg-gray-50 dark:bg-gray-700">
223
- <tr>
224
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider w-32">Status</th>
225
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Task</th>
226
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider w-24">Queue</th>
227
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider w-20">Duration</th>
228
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider w-24">Updated</th>
229
- <th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider w-32">Actions</th>
230
- </tr>
231
- </thead>
232
- <tbody class="divide-y divide-gray-200 dark:divide-gray-600">
233
- ${filteredTasks.map(task => this.renderTaskRow(task)).join('')}
234
- </tbody>
235
- </table>
236
- </div>
237
- `;
238
- }
239
-
240
- renderTaskRow(task) {
241
- // Map Dramatiq statuses to our display statuses
242
- const statusMap = {
243
- 'enqueued': 'pending',
244
- 'delayed': 'pending',
245
- 'running': 'running',
246
- 'done': 'done',
247
- 'failed': 'failed',
248
- 'skipped': 'done'
249
- };
250
-
251
- const displayStatus = statusMap[task.status.toLowerCase()] || task.status.toLowerCase();
252
-
253
- const statusColors = {
254
- pending: 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200',
255
- running: 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200',
256
- done: 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200',
257
- failed: 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200'
258
- };
259
-
260
- const statusIcons = {
261
- pending: 'schedule',
262
- running: 'play_circle',
263
- done: 'check_circle',
264
- failed: 'error'
265
- };
266
-
267
- const statusLabels = {
268
- pending: task.status.toLowerCase() === 'delayed' ? 'DELAYED' : 'PENDING',
269
- running: 'RUNNING',
270
- done: 'DONE',
271
- failed: 'FAILED'
272
- };
273
-
274
- const duration = this.calculateDuration(task.created_at, task.updated_at);
275
- const updatedTime = new Date(task.updated_at).toLocaleTimeString();
276
-
277
- return `
278
- <tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
279
- <!-- Status -->
280
- <td class="px-4 py-3 whitespace-nowrap">
281
- <div class="flex items-center space-x-2">
282
- <span class="material-icons text-sm ${displayStatus === 'failed' ? 'text-red-500' : displayStatus === 'done' ? 'text-green-500' : displayStatus === 'running' ? 'text-blue-500' : 'text-yellow-500'}">${statusIcons[displayStatus]}</span>
283
- <span class="px-2 py-1 text-xs font-medium rounded-full ${statusColors[displayStatus]}">${statusLabels[displayStatus]}</span>
284
- </div>
285
- ${displayStatus === 'running' && task.progress ? `
286
- <div class="mt-1 w-16 bg-gray-200 dark:bg-gray-600 rounded-full h-1">
287
- <div class="bg-blue-600 h-1 rounded-full transition-all duration-300" style="width: ${task.progress}%"></div>
288
- </div>
289
- ` : ''}
290
- </td>
291
-
292
- <!-- Task -->
293
- <td class="px-4 py-3">
294
- <div class="text-sm font-medium text-gray-900 dark:text-white">${task.actor_name}</div>
295
- <div class="text-xs text-gray-500 dark:text-gray-400 font-mono">${task.id.substring(0, 8)}...</div>
296
- </td>
297
-
298
- <!-- Queue -->
299
- <td class="px-4 py-3 whitespace-nowrap">
300
- <span class="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 rounded-full">${task.queue}</span>
301
- </td>
302
-
303
- <!-- Duration -->
304
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">${duration}</td>
305
-
306
- <!-- Updated -->
307
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">${updatedTime}</td>
308
-
309
- <!-- Actions -->
310
- <td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
311
- <div class="flex items-center justify-end space-x-1">
312
- ${task.args || task.kwargs ? `
313
- <button class="p-1 text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded transition-colors"
314
- onclick="this.classList.toggle('active'); const row = this.closest('tr'); const nextRow = row.nextElementSibling; if (nextRow && nextRow.classList.contains('details-row')) { nextRow.remove(); } else { row.insertAdjacentHTML('afterend', \`<tr class='details-row'><td colspan='6' class='px-4 py-2 bg-gray-50 dark:bg-gray-900 text-xs border-t border-gray-200 dark:border-gray-600'><strong>Args:</strong> \${task.args || 'None'}<br><strong>Kwargs:</strong> \${task.kwargs || 'None'}</td></tr>\`); }"
315
- title="Show arguments">
316
- <span class="material-icons text-sm">code</span>
317
- </button>
318
- ` : ''}
319
- ${task.result ? `
320
- <button class="p-1 text-green-600 dark:text-green-400 hover:text-green-900 dark:hover:text-green-300 hover:bg-green-50 dark:hover:bg-green-900/20 rounded transition-colors"
321
- onclick="alert('Result: ' + \`${task.result}\`)"
322
- title="Show result">
323
- <span class="material-icons text-sm">check_circle</span>
324
- </button>
325
- ` : ''}
326
- ${task.traceback ? `
327
- <button class="p-1 text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors"
328
- onclick="alert('Error: ' + \`${task.traceback}\`)"
329
- title="Show error">
330
- <span class="material-icons text-sm">error</span>
331
- </button>
332
- ` : ''}
333
- ${displayStatus === 'failed' ? `
334
- <button class="px-2 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors ml-1">
335
- Retry
336
- </button>
337
- ` : ''}
338
- </div>
339
- </td>
340
- </tr>
341
- `;
342
- }
343
-
344
- calculateDuration(startTime, endTime) {
345
- const start = new Date(startTime);
346
- const end = new Date(endTime);
347
- const diff = end - start;
348
-
349
- if (diff < 1000) return `${diff}ms`;
350
- if (diff < 60000) return `${Math.floor(diff / 1000)}s`;
351
- if (diff < 3600000) return `${Math.floor(diff / 60000)}m ${Math.floor((diff % 60000) / 1000)}s`;
352
- return `${Math.floor(diff / 3600000)}h ${Math.floor((diff % 3600000) / 60000)}m`;
353
- }
354
-
355
- updateTaskCount() {
356
- const badge = document.getElementById('task-count-badge');
357
- if (badge) {
358
- const filteredCount = this.getFilteredTasks().length;
359
- const totalCount = this.tasks.length;
360
- badge.textContent = filteredCount === totalCount ?
361
- `${totalCount} tasks` :
362
- `${filteredCount} of ${totalCount} tasks`;
363
- }
364
- }
365
-
366
- updateLastUpdateTime() {
367
- const timeElement = document.getElementById('last-update-time');
368
- if (timeElement) {
369
- timeElement.textContent = new Date().toLocaleTimeString();
370
- }
371
- }
372
-
373
- showLoading() {
374
- const loading = document.getElementById('tasks-loading');
375
- const empty = document.getElementById('tasks-empty');
376
- const list = document.getElementById('tasks-list');
377
-
378
- loading?.classList.remove('hidden');
379
- empty?.classList.add('hidden');
380
- if (list) list.innerHTML = '';
381
- }
382
-
383
- showError(message) {
384
- const container = document.getElementById('tasks-list');
385
- if (container) {
386
- container.innerHTML = `
387
- <div class="text-center py-12">
388
- <span class="material-icons text-6xl text-red-400 dark:text-red-600 mb-4">error</span>
389
- <h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Error Loading Tasks</h3>
390
- <p class="text-gray-500 dark:text-gray-400">${message}</p>
391
- </div>
392
- `;
393
- }
394
- }
395
-
396
- async clearCompletedTasks() {
397
- if (!confirm('Are you sure you want to clear all completed tasks?')) {
398
- return;
399
- }
400
-
401
- try {
402
- // TODO: Implement API call to clear completed tasks
403
- console.log('Clearing completed tasks...');
404
- this.loadTasks();
405
- } catch (error) {
406
- console.error('Failed to clear completed tasks:', error);
407
- }
408
- }
409
-
410
- exportTasks() {
411
- const filteredTasks = this.getFilteredTasks();
412
- const csv = this.tasksToCSV(filteredTasks);
413
- this.downloadCSV(csv, 'tasks-export.csv');
414
- }
415
-
416
- tasksToCSV(tasks) {
417
- const headers = ['ID', 'Actor', 'Status', 'Queue', 'Created', 'Updated', 'Duration'];
418
- const rows = tasks.map(task => [
419
- task.id,
420
- task.actor_name,
421
- task.status,
422
- task.queue,
423
- new Date(task.created_at).toLocaleString(),
424
- new Date(task.updated_at).toLocaleString(),
425
- this.calculateDuration(task.created_at, task.updated_at)
426
- ]);
427
-
428
- return [headers, ...rows].map(row =>
429
- row.map(field => `"${field}"`).join(',')
430
- ).join('\n');
431
- }
432
-
433
- downloadCSV(csv, filename) {
434
- const blob = new Blob([csv], { type: 'text/csv' });
435
- const url = window.URL.createObjectURL(blob);
436
- const a = document.createElement('a');
437
- a.href = url;
438
- a.download = filename;
439
- a.click();
440
- window.URL.revokeObjectURL(url);
441
- }
442
- }
443
-
444
- // Initialize task monitor when DOM is loaded
445
- document.addEventListener('DOMContentLoaded', () => {
446
- window.taskMonitor = new TaskMonitor();
447
- });
448
-
449
- // Global function to show task monitor
450
- window.showTaskMonitor = () => {
451
- if (window.taskMonitor) {
452
- window.taskMonitor.show();
453
- }
454
- };
@@ -1,77 +0,0 @@
1
- /**
2
- * Theme Toggle Functionality
3
- * Handles dark/light mode switching
4
- */
5
-
6
- class ThemeManager {
7
- constructor() {
8
- this.init();
9
- }
10
-
11
- init() {
12
- // Get saved theme or default to light
13
- this.currentTheme = localStorage.getItem('theme') || 'light';
14
-
15
- // Apply theme on load
16
- this.applyTheme(this.currentTheme);
17
-
18
- // Setup toggle button
19
- this.setupToggle();
20
- }
21
-
22
- applyTheme(theme) {
23
- const html = document.documentElement;
24
- const toggleButton = document.getElementById('theme-toggle');
25
- const icon = toggleButton?.querySelector('.material-icons');
26
-
27
- console.log('Applying theme:', theme);
28
- console.log('Toggle button found:', !!toggleButton);
29
- console.log('Icon found:', !!icon);
30
-
31
- if (theme === 'dark') {
32
- html.classList.add('dark');
33
- if (icon) {
34
- icon.textContent = 'dark_mode';
35
- }
36
- } else {
37
- html.classList.remove('dark');
38
- if (icon) {
39
- icon.textContent = 'light_mode';
40
- }
41
- }
42
-
43
- // Save to localStorage
44
- localStorage.setItem('theme', theme);
45
- this.currentTheme = theme;
46
-
47
- console.log('Theme applied. Current classes:', html.classList.toString());
48
- }
49
-
50
- toggle() {
51
- const newTheme = this.currentTheme === 'light' ? 'dark' : 'light';
52
- this.applyTheme(newTheme);
53
- }
54
-
55
- setupToggle() {
56
- const toggleButton = document.getElementById('theme-toggle');
57
- if (toggleButton) {
58
- toggleButton.addEventListener('click', () => this.toggle());
59
- console.log('Theme toggle button found and event listener added');
60
- } else {
61
- console.warn('Theme toggle button not found');
62
- // Try to find it after a short delay
63
- setTimeout(() => {
64
- const delayedButton = document.getElementById('theme-toggle');
65
- if (delayedButton) {
66
- delayedButton.addEventListener('click', () => this.toggle());
67
- console.log('Theme toggle button found after delay');
68
- }
69
- }, 100);
70
- }
71
- }
72
- }
73
-
74
- // Initialize theme manager when DOM is loaded
75
- document.addEventListener('DOMContentLoaded', () => {
76
- window.themeManager = new ThemeManager();
77
- });
@@ -1,96 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en" class="h-full">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>{% block title %}Dramatiq Tasks Dashboard{% endblock %}</title>
7
-
8
- <!-- Tailwind CSS -->
9
- <script src="https://cdn.tailwindcss.com"></script>
10
- <script>
11
- tailwind.config = {
12
- darkMode: 'class',
13
- theme: {
14
- extend: {
15
- colors: {
16
- primary: {
17
- 50: '#eff6ff',
18
- 100: '#dbeafe',
19
- 200: '#bfdbfe',
20
- 300: '#93c5fd',
21
- 400: '#60a5fa',
22
- 500: '#3b82f6',
23
- 600: '#2563eb',
24
- 700: '#1d4ed8',
25
- 800: '#1e40af',
26
- 900: '#1e3a8a',
27
- }
28
- }
29
- }
30
- }
31
- }
32
- </script>
33
-
34
- <!-- Material Icons -->
35
- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
36
-
37
- <!-- Custom CSS -->
38
- {% load static %}
39
- <link rel="stylesheet" href="{% static 'tasks/css/dashboard.css' %}">
40
-
41
- {% block extra_head %}{% endblock %}
42
- </head>
43
- <body class="h-full bg-gray-50 dark:bg-gray-900">
44
- <div class="min-h-full">
45
- <!-- Header -->
46
- <header class="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700">
47
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
48
- <div class="flex items-center justify-between h-16">
49
- <div class="flex items-center space-x-3">
50
- <div class="flex-shrink-0">
51
- <span class="material-icons text-primary-600 dark:text-primary-400 text-3xl">analytics</span>
52
- </div>
53
- <div>
54
- <h1 class="text-xl font-bold text-gray-900 dark:text-white">
55
- {% block header_title %}Dramatiq Tasks Dashboard{% endblock %}
56
- </h1>
57
- <p class="text-sm text-gray-500 dark:text-gray-400">
58
- {% block header_subtitle %}Monitor and manage background task queues{% endblock %}
59
- </p>
60
- </div>
61
- </div>
62
- <div class="flex items-center space-x-3">
63
- {% block header_actions %}
64
- <a href="/admin/django_dramatiq/task/"
65
- class="inline-flex items-center px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors duration-200">
66
- <span class="material-icons text-sm mr-2">history</span>
67
- Task History
68
- </a>
69
- <button id="theme-toggle"
70
- class="group inline-flex items-center justify-center px-3 py-2 rounded-md bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
71
- <span class="material-icons text-gray-600 dark:text-yellow-400 group-hover:text-primary-600 dark:group-hover:text-yellow-300 transition-colors duration-200">light_mode</span>
72
- </button>
73
- {% endblock %}
74
- </div>
75
- </div>
76
- </div>
77
- </header>
78
-
79
- <!-- Main content -->
80
- <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
81
- {% block content %}{% endblock %}
82
- </main>
83
- </div>
84
-
85
- <!-- Notification Container -->
86
- <div id="notification-container" class="fixed top-4 left-1/2 transform -translate-x-1/2 z-50 space-y-2"></div>
87
-
88
- <!-- Modal Container -->
89
- <div id="modal-container"></div>
90
-
91
- <!-- JavaScript -->
92
- <script src="{% static 'tasks/js/theme.js' %}"></script>
93
- <script src="{% static 'tasks/js/notifications.js' %}"></script>
94
- {% block extra_js %}{% endblock %}
95
- </body>
96
- </html>