django-cfg 1.3.9__py3-none-any.whl → 1.3.11__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 (187) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  3. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  4. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  5. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  13. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  14. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  15. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  16. django_cfg/apps/payments/config/__init__.py +14 -15
  17. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  18. django_cfg/apps/payments/config/helpers.py +8 -13
  19. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  20. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  21. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  22. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  23. django_cfg/apps/payments/models/payments.py +94 -0
  24. django_cfg/apps/payments/services/core/base.py +4 -4
  25. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  26. django_cfg/apps/payments/services/providers/base.py +209 -3
  27. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  28. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  29. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  31. django_cfg/apps/payments/services/providers/registry.py +5 -5
  32. django_cfg/apps/payments/services/types/requests.py +19 -7
  33. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  34. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  35. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  36. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  37. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  38. django_cfg/apps/payments/urls.py +3 -2
  39. django_cfg/apps/payments/views/api/currencies.py +3 -0
  40. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  41. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  42. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  43. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  44. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  45. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  47. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  52. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  53. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  54. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  55. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  56. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  57. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  58. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  59. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  60. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  61. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  62. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  63. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  66. django_cfg/apps/tasks/urls.py +2 -2
  67. django_cfg/apps/tasks/urls_admin.py +2 -2
  68. django_cfg/apps/tasks/utils/__init__.py +1 -0
  69. django_cfg/apps/tasks/utils/simulator.py +356 -0
  70. django_cfg/apps/tasks/views/__init__.py +16 -0
  71. django_cfg/apps/tasks/views/api.py +569 -0
  72. django_cfg/apps/tasks/views/dashboard.py +58 -0
  73. django_cfg/core/integration/__init__.py +21 -0
  74. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  75. django_cfg/models/constance.py +0 -11
  76. django_cfg/models/payments.py +137 -3
  77. django_cfg/modules/django_tasks.py +54 -21
  78. django_cfg/registry/core.py +4 -9
  79. django_cfg/template_archive/django_sample.zip +0 -0
  80. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -2
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/RECORD +84 -152
  82. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  83. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  84. django_cfg/apps/payments/config/constance/fields.py +0 -69
  85. django_cfg/apps/payments/config/constance/settings.py +0 -160
  86. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  87. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  88. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  89. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  90. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  91. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  92. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  93. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  94. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  95. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  96. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  97. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  98. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  99. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  100. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  101. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  102. django_cfg/apps/tasks/views.py +0 -461
  103. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  104. django_cfg/management/commands/app_agent_generate.py +0 -342
  105. django_cfg/management/commands/app_agent_info.py +0 -308
  106. django_cfg/management/commands/auto_generate.py +0 -486
  107. django_cfg/modules/django_app_agent/__init__.py +0 -87
  108. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  109. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  110. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  111. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  112. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  113. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  114. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  119. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  120. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  121. django_cfg/modules/django_app_agent/core/config.py +0 -300
  122. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  123. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  124. django_cfg/modules/django_app_agent/models/base.py +0 -283
  125. django_cfg/modules/django_app_agent/models/context.py +0 -496
  126. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  127. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  128. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  129. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  130. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  131. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  132. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  133. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  134. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  135. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  136. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  137. django_cfg/modules/django_app_agent/services/base.py +0 -437
  138. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  139. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  140. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  141. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  142. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  143. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  144. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  145. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  146. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  147. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  148. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  149. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  150. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  151. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  152. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  153. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  154. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  155. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  156. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  157. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  158. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  159. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  160. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  161. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  171. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  172. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  173. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  174. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  175. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  176. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  177. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  178. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  179. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  180. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  181. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  182. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  183. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  184. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  185. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.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>