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
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Queues Dashboard Module
3
+ * Handles queues tab functionality
4
+ */
5
+ export class QueuesModule {
6
+ constructor(api, dashboard) {
7
+ this.api = api;
8
+ this.dashboard = dashboard;
9
+ }
10
+
11
+ /**
12
+ * Load queues data
13
+ */
14
+ async loadData() {
15
+ try {
16
+ console.log('Loading queues data...');
17
+
18
+ const response = await this.api.getQueueStatus();
19
+ const data = response.data || response;
20
+
21
+ this.renderQueuesData(data);
22
+
23
+ } catch (error) {
24
+ console.error('Failed to load queues data:', error);
25
+ this.showError('Failed to load queues data');
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Render queues data
31
+ */
32
+ renderQueuesData(data) {
33
+ const container = document.getElementById('queues-container');
34
+ if (!container) return;
35
+
36
+ if (!data.queues || Object.keys(data.queues).length === 0) {
37
+ container.innerHTML = `
38
+ <div class="text-center py-8">
39
+ <span class="material-icons text-4xl text-gray-400 mb-4">queue</span>
40
+ <p class="text-gray-500 dark:text-gray-400">No active queues found</p>
41
+ <p class="text-sm text-gray-400 dark:text-gray-500 mt-2">Queues will appear here when tasks are processed</p>
42
+ </div>
43
+ `;
44
+ return;
45
+ }
46
+
47
+ let html = '<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">';
48
+
49
+ Object.entries(data.queues).forEach(([queueName, queueInfo]) => {
50
+ const totalTasks = (queueInfo.pending || 0) + (queueInfo.failed || 0) + (queueInfo.processed || 0);
51
+
52
+ html += `
53
+ <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 border border-gray-200 dark:border-gray-600 hover:shadow-md transition-shadow">
54
+ <div class="flex items-center justify-between mb-3">
55
+ <div class="flex items-center">
56
+ <span class="material-icons text-blue-600 dark:text-blue-400 mr-2">queue</span>
57
+ <h4 class="font-medium text-gray-900 dark:text-white">${queueName}</h4>
58
+ </div>
59
+ <span class="px-2 py-1 text-xs ${totalTasks > 0 ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' : 'bg-gray-100 dark:bg-gray-600 text-gray-600 dark:text-gray-300'} rounded-full">
60
+ ${totalTasks > 0 ? 'Active' : 'Idle'}
61
+ </span>
62
+ </div>
63
+
64
+ <div class="space-y-2 text-sm">
65
+ <div class="flex justify-between">
66
+ <span class="text-gray-600 dark:text-gray-400">Pending:</span>
67
+ <span class="font-medium text-yellow-600 dark:text-yellow-400">${queueInfo.pending || 0}</span>
68
+ </div>
69
+ <div class="flex justify-between">
70
+ <span class="text-gray-600 dark:text-gray-400">Failed:</span>
71
+ <span class="font-medium text-red-600 dark:text-red-400">${queueInfo.failed || 0}</span>
72
+ </div>
73
+ <div class="flex justify-between">
74
+ <span class="text-gray-600 dark:text-gray-400">Processed:</span>
75
+ <span class="font-medium text-green-600 dark:text-green-400">${queueInfo.processed || 0}</span>
76
+ </div>
77
+ </div>
78
+
79
+ ${totalTasks > 0 ? `
80
+ <div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600">
81
+ <div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
82
+ <div class="bg-gradient-to-r from-green-400 to-blue-500 h-2 rounded-full transition-all duration-300"
83
+ style="width: ${Math.min(100, (queueInfo.processed || 0) / totalTasks * 100)}%"></div>
84
+ </div>
85
+ <div class="flex justify-between text-xs text-gray-500 dark:text-gray-400 mt-1">
86
+ <span>Progress</span>
87
+ <span>${Math.round((queueInfo.processed || 0) / totalTasks * 100)}%</span>
88
+ </div>
89
+ </div>
90
+ ` : ''}
91
+
92
+ <div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600">
93
+ <div class="flex items-center text-xs text-gray-500 dark:text-gray-400">
94
+ <span class="material-icons text-sm mr-1">schedule</span>
95
+ Last activity: ${queueInfo.last_activity ? new Date(queueInfo.last_activity).toLocaleTimeString() : 'Never'}
96
+ </div>
97
+ </div>
98
+ </div>
99
+ `;
100
+ });
101
+
102
+ html += '</div>';
103
+ container.innerHTML = html;
104
+ }
105
+
106
+ /**
107
+ * Show error message
108
+ */
109
+ showError(message) {
110
+ const container = document.getElementById('queues-container');
111
+ if (container) {
112
+ container.innerHTML = `
113
+ <div class="text-center py-8">
114
+ <span class="material-icons text-4xl text-red-400 mb-4">error</span>
115
+ <p class="text-red-600 dark:text-red-400">${message}</p>
116
+ </div>
117
+ `;
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,350 @@
1
+ /**
2
+ * Tasks Dashboard Module
3
+ * Handles tasks tab functionality with filtering
4
+ */
5
+ export class TasksModule {
6
+ constructor(api, dashboard) {
7
+ this.api = api;
8
+ this.dashboard = dashboard; // Reference to main dashboard for badge updates
9
+ this.allTasks = [];
10
+ this.filteredTasks = [];
11
+ this.filters = {
12
+ status: '',
13
+ queue: '',
14
+ search: ''
15
+ };
16
+ this.setupEventListeners();
17
+ }
18
+
19
+ /**
20
+ * Setup event listeners for filters
21
+ */
22
+ setupEventListeners() {
23
+ // Task filters
24
+ document.getElementById('task-status-filter')?.addEventListener('change', (e) => {
25
+ this.filters.status = e.target.value;
26
+ this.applyFilters();
27
+ });
28
+
29
+ document.getElementById('task-queue-filter')?.addEventListener('change', (e) => {
30
+ this.filters.queue = e.target.value;
31
+ this.applyFilters();
32
+ });
33
+
34
+ document.getElementById('task-search-input')?.addEventListener('input', (e) => {
35
+ this.filters.search = e.target.value.toLowerCase();
36
+ this.applyFilters();
37
+ });
38
+
39
+ // Task actions
40
+ document.getElementById('clear-completed-tasks')?.addEventListener('click', () => this.clearCompletedTasks());
41
+ document.getElementById('export-tasks-csv')?.addEventListener('click', () => this.exportTasks());
42
+
43
+ // Auto-refresh toggle
44
+ document.getElementById('tasks-auto-refresh-toggle')?.addEventListener('click', () => this.toggleAutoRefresh());
45
+ document.getElementById('refresh-tasks-btn')?.addEventListener('click', () => this.loadData());
46
+ }
47
+
48
+ /**
49
+ * Load tasks data
50
+ */
51
+ async loadData() {
52
+ try {
53
+ console.log('Loading tasks data...');
54
+
55
+ const response = await this.api.getTaskList();
56
+ const data = response.data || response;
57
+
58
+ if (data.tasks && data.tasks.length > 0) {
59
+ this.allTasks = data.tasks;
60
+ } else {
61
+ // Generate mock data for demonstration
62
+ this.allTasks = this.generateMockTasks();
63
+ }
64
+
65
+ // Apply current filters and render
66
+ this.applyFilters();
67
+ this.updateLastUpdateTime();
68
+
69
+ } catch (error) {
70
+ console.error('Failed to load task data:', error);
71
+ this.allTasks = this.generateMockTasks();
72
+ this.applyFilters();
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Generate mock tasks for demonstration
78
+ */
79
+ generateMockTasks() {
80
+ const statuses = ['pending', 'running', 'completed', 'failed'];
81
+ const queues = ['default', 'high', 'low', 'background', 'payments', 'critical'];
82
+ const actors = ['process_document_async', 'send_notification', 'cleanup_old_files', 'generate_report', 'sync_data', 'send_email_task'];
83
+
84
+ return Array.from({ length: 20 }, (_, i) => ({
85
+ id: `task_${i + 1}`,
86
+ actor_name: actors[Math.floor(Math.random() * actors.length)],
87
+ status: statuses[Math.floor(Math.random() * statuses.length)],
88
+ queue_name: queues[Math.floor(Math.random() * queues.length)],
89
+ created_at: new Date(Date.now() - Math.random() * 86400000).toISOString(),
90
+ updated_at: new Date(Date.now() - Math.random() * 3600000).toISOString(),
91
+ message_id: `msg_${String(i + 1).padStart(3, '0')}`,
92
+ progress: Math.random() > 0.7 ? Math.floor(Math.random() * 100) : null
93
+ }));
94
+ }
95
+
96
+ /**
97
+ * Apply filters to tasks
98
+ */
99
+ applyFilters() {
100
+ this.filteredTasks = this.allTasks.filter(task => {
101
+ const matchesStatus = !this.filters.status || this.mapStatus(task.status) === this.filters.status;
102
+ const matchesQueue = !this.filters.queue || (task.queue_name || task.queue) === this.filters.queue;
103
+ const matchesSearch = !this.filters.search ||
104
+ task.actor_name.toLowerCase().includes(this.filters.search) ||
105
+ String(task.id).toLowerCase().includes(this.filters.search);
106
+
107
+ return matchesStatus && matchesQueue && matchesSearch;
108
+ });
109
+
110
+ this.renderTasks();
111
+ this.updateTaskCounts();
112
+ }
113
+
114
+ /**
115
+ * Map task status to display status
116
+ */
117
+ mapStatus(status) {
118
+ const statusMap = {
119
+ 'enqueued': 'pending',
120
+ 'delayed': 'pending',
121
+ 'pending': 'pending',
122
+ 'running': 'running',
123
+ 'done': 'completed',
124
+ 'completed': 'completed',
125
+ 'failed': 'failed',
126
+ 'skipped': 'completed'
127
+ };
128
+
129
+ return statusMap[status?.toLowerCase()] || 'pending';
130
+ }
131
+
132
+ /**
133
+ * Render tasks table
134
+ */
135
+ renderTasks() {
136
+ const tableBody = document.querySelector('#task-table-body');
137
+ if (!tableBody) return;
138
+
139
+ // Show/hide empty state
140
+ const emptyState = document.getElementById('tasks-empty');
141
+ const loadingState = document.getElementById('tasks-loading');
142
+
143
+ loadingState?.classList.add('hidden');
144
+
145
+ if (this.filteredTasks.length === 0) {
146
+ emptyState?.classList.remove('hidden');
147
+ tableBody.innerHTML = '';
148
+ return;
149
+ }
150
+
151
+ emptyState?.classList.add('hidden');
152
+ tableBody.innerHTML = '';
153
+
154
+ this.filteredTasks.forEach(task => {
155
+ const row = this.createTaskRow(task);
156
+ tableBody.appendChild(row);
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Create task row element
162
+ */
163
+ createTaskRow(task) {
164
+ const row = document.createElement('tr');
165
+ row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors';
166
+ row.setAttribute('data-task-id', task.id);
167
+
168
+ const displayStatus = this.mapStatus(task.status);
169
+ const statusConfig = this.getStatusConfig(displayStatus);
170
+
171
+ row.innerHTML = `
172
+ <td class="px-4 py-3 whitespace-nowrap">
173
+ <div class="flex items-center space-x-2">
174
+ <span class="material-icons text-sm ${statusConfig.iconColor}">${statusConfig.icon}</span>
175
+ <span class="px-2 py-1 text-xs font-medium rounded-full ${statusConfig.color}">
176
+ ${statusConfig.label}
177
+ </span>
178
+ </div>
179
+ ${displayStatus === 'running' && task.progress ? `
180
+ <div class="mt-1 w-16 bg-gray-200 dark:bg-gray-600 rounded-full h-1">
181
+ <div class="bg-blue-600 h-1 rounded-full transition-all duration-300" style="width: ${task.progress}%"></div>
182
+ </div>
183
+ ` : ''}
184
+ </td>
185
+ <td class="px-4 py-3">
186
+ <div class="text-sm font-medium text-gray-900 dark:text-white">${task.actor_name}</div>
187
+ <div class="text-xs text-gray-500 dark:text-gray-400 font-mono">${String(task.id).substring(0, 8)}...</div>
188
+ </td>
189
+ <td class="px-4 py-3 whitespace-nowrap">
190
+ <span class="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 rounded-full">
191
+ ${task.queue_name || task.queue || 'unknown'}
192
+ </span>
193
+ </td>
194
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
195
+ ${this.calculateDuration(task.created_at, task.updated_at)}
196
+ </td>
197
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
198
+ ${new Date(task.updated_at).toLocaleTimeString()}
199
+ </td>
200
+ <td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
201
+ <div class="flex items-center justify-end space-x-1">
202
+ ${displayStatus === 'failed' ? `
203
+ <button class="px-2 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors">
204
+ Retry
205
+ </button>
206
+ ` : ''}
207
+ <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" title="View details">
208
+ <span class="material-icons text-sm">info</span>
209
+ </button>
210
+ </div>
211
+ </td>
212
+ `;
213
+
214
+ return row;
215
+ }
216
+
217
+ /**
218
+ * Get status configuration
219
+ */
220
+ getStatusConfig(status) {
221
+ const configs = {
222
+ pending: {
223
+ icon: 'schedule',
224
+ label: 'PENDING',
225
+ color: 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200',
226
+ iconColor: 'text-yellow-500'
227
+ },
228
+ running: {
229
+ icon: 'play_circle',
230
+ label: 'RUNNING',
231
+ color: 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200',
232
+ iconColor: 'text-blue-500'
233
+ },
234
+ completed: {
235
+ icon: 'check_circle',
236
+ label: 'COMPLETED',
237
+ color: 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200',
238
+ iconColor: 'text-green-500'
239
+ },
240
+ failed: {
241
+ icon: 'error',
242
+ label: 'FAILED',
243
+ color: 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200',
244
+ iconColor: 'text-red-500'
245
+ }
246
+ };
247
+
248
+ return configs[status] || configs.pending;
249
+ }
250
+
251
+ /**
252
+ * Update task counts in footer
253
+ */
254
+ updateTaskCounts() {
255
+ const counts = {
256
+ completed: this.filteredTasks.filter(t => ['completed', 'done'].includes(this.mapStatus(t.status))).length,
257
+ running: this.filteredTasks.filter(t => this.mapStatus(t.status) === 'running').length,
258
+ pending: this.filteredTasks.filter(t => this.mapStatus(t.status) === 'pending').length,
259
+ failed: this.filteredTasks.filter(t => this.mapStatus(t.status) === 'failed').length
260
+ };
261
+
262
+ document.getElementById('completed-count').textContent = counts.completed;
263
+ document.getElementById('running-count').textContent = counts.running;
264
+ document.getElementById('pending-count').textContent = counts.pending;
265
+ document.getElementById('failed-count').textContent = counts.failed;
266
+
267
+ // Update task count badge
268
+ const badge = document.getElementById('task-count-badge');
269
+ if (badge) {
270
+ const filteredCount = this.filteredTasks.length;
271
+ const totalCount = this.allTasks.length;
272
+ badge.textContent = filteredCount === totalCount ?
273
+ `${totalCount} tasks` :
274
+ `${filteredCount} of ${totalCount} tasks`;
275
+ }
276
+
277
+ // Update tab badge
278
+ const tabBadge = document.querySelector('#tasks-count-badge');
279
+ if (tabBadge) {
280
+ tabBadge.textContent = this.allTasks.length;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Calculate duration between dates
286
+ */
287
+ calculateDuration(start, end) {
288
+ if (!start || !end) return '--';
289
+ const diff = new Date(end) - new Date(start);
290
+ if (diff < 60000) return `${Math.floor(diff / 1000)}s`;
291
+ if (diff < 3600000) return `${Math.floor(diff / 60000)}m`;
292
+ return `${Math.floor(diff / 3600000)}h`;
293
+ }
294
+
295
+ /**
296
+ * Update last update time
297
+ */
298
+ updateLastUpdateTime() {
299
+ const timeElement = document.getElementById('tasks-last-update-time');
300
+ if (timeElement) {
301
+ timeElement.textContent = new Date().toLocaleTimeString();
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Task actions
307
+ */
308
+ clearCompletedTasks() {
309
+ if (!confirm('Are you sure you want to clear all completed tasks?')) {
310
+ return;
311
+ }
312
+ console.log('Clearing completed tasks...');
313
+ }
314
+
315
+ exportTasks() {
316
+ const csv = this.tasksToCSV(this.filteredTasks);
317
+ this.downloadCSV(csv, 'tasks-export.csv');
318
+ }
319
+
320
+ tasksToCSV(tasks) {
321
+ const headers = ['ID', 'Actor', 'Status', 'Queue', 'Created', 'Updated', 'Duration'];
322
+ const rows = tasks.map(task => [
323
+ task.id,
324
+ task.actor_name,
325
+ task.status,
326
+ task.queue_name || task.queue || 'unknown',
327
+ new Date(task.created_at).toLocaleString(),
328
+ new Date(task.updated_at).toLocaleString(),
329
+ this.calculateDuration(task.created_at, task.updated_at)
330
+ ]);
331
+
332
+ return [headers, ...rows].map(row =>
333
+ row.map(field => `"${field}"`).join(',')
334
+ ).join('\n');
335
+ }
336
+
337
+ downloadCSV(csv, filename) {
338
+ const blob = new Blob([csv], { type: 'text/csv' });
339
+ const url = window.URL.createObjectURL(blob);
340
+ const a = document.createElement('a');
341
+ a.href = url;
342
+ a.download = filename;
343
+ a.click();
344
+ window.URL.revokeObjectURL(url);
345
+ }
346
+
347
+ toggleAutoRefresh() {
348
+ console.log('Toggling auto-refresh...');
349
+ }
350
+ }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Workers Dashboard Module
3
+ * Handles workers tab functionality
4
+ */
5
+ export class WorkersModule {
6
+ constructor(api, dashboard) {
7
+ this.api = api;
8
+ this.dashboard = dashboard;
9
+ }
10
+
11
+ /**
12
+ * Load workers data
13
+ */
14
+ async loadData() {
15
+ try {
16
+ console.log('Loading workers data...');
17
+
18
+ const response = await this.api.getWorkersList();
19
+ const data = response.data || response;
20
+
21
+ this.renderWorkersData(data);
22
+
23
+ } catch (error) {
24
+ console.error('Failed to load workers data:', error);
25
+ this.showError('Failed to load workers data');
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Render workers data
31
+ */
32
+ renderWorkersData(data) {
33
+ const container = document.getElementById('workers-container');
34
+ if (!container) return;
35
+
36
+ // Check if we have workers data
37
+ let workers = [];
38
+ if (data.workers && Array.isArray(data.workers)) {
39
+ workers = data.workers;
40
+ } else if (data.workers && typeof data.workers === 'number' && data.workers > 0) {
41
+ // If we just have a count, generate mock worker data
42
+ workers = Array.from({ length: data.workers }, (_, i) => ({
43
+ id: i + 1,
44
+ pid: Math.floor(Math.random() * 10000) + 1000,
45
+ threads: 2,
46
+ tasks_processed: Math.floor(Math.random() * 100),
47
+ started_at: new Date(Date.now() - Math.random() * 86400000).toISOString(),
48
+ status: 'active'
49
+ }));
50
+ }
51
+
52
+ if (workers.length === 0) {
53
+ container.innerHTML = `
54
+ <div class="text-center py-8">
55
+ <span class="material-icons text-4xl text-gray-400 mb-4">engineering</span>
56
+ <p class="text-gray-500 dark:text-gray-400">No active workers found</p>
57
+ <p class="text-sm text-gray-400 dark:text-gray-500 mt-2">Start workers to see them here</p>
58
+ <div class="mt-4">
59
+ <button class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded-lg transition-colors">
60
+ <span class="material-icons text-sm mr-1">play_arrow</span>
61
+ Start Workers
62
+ </button>
63
+ </div>
64
+ </div>
65
+ `;
66
+ return;
67
+ }
68
+
69
+ let html = '<div class="grid grid-cols-1 md:grid-cols-2 gap-4">';
70
+
71
+ workers.forEach((worker, index) => {
72
+ const uptime = this.calculateUptime(worker.started_at);
73
+ const isActive = worker.status === 'active' || !worker.status;
74
+
75
+ html += `
76
+ <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 border border-gray-200 dark:border-gray-600 hover:shadow-md transition-shadow">
77
+ <div class="flex items-center justify-between mb-3">
78
+ <div class="flex items-center">
79
+ <span class="material-icons ${isActive ? 'text-green-600 dark:text-green-400' : 'text-gray-400'} mr-2">engineering</span>
80
+ <h4 class="font-medium text-gray-900 dark:text-white">Worker ${worker.id || index + 1}</h4>
81
+ </div>
82
+ <div class="flex items-center">
83
+ <div class="w-2 h-2 ${isActive ? 'bg-green-500' : 'bg-gray-400'} rounded-full mr-2"></div>
84
+ <span class="px-2 py-1 text-xs ${isActive ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' : 'bg-gray-100 dark:bg-gray-600 text-gray-600 dark:text-gray-300'} rounded-full">
85
+ ${isActive ? 'Active' : 'Inactive'}
86
+ </span>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="space-y-2 text-sm">
91
+ <div class="flex justify-between">
92
+ <span class="text-gray-600 dark:text-gray-400">Process ID:</span>
93
+ <span class="font-mono text-gray-900 dark:text-white">${worker.pid || 'N/A'}</span>
94
+ </div>
95
+ <div class="flex justify-between">
96
+ <span class="text-gray-600 dark:text-gray-400">Threads:</span>
97
+ <span class="font-medium text-blue-600 dark:text-blue-400">${worker.threads || 2}</span>
98
+ </div>
99
+ <div class="flex justify-between">
100
+ <span class="text-gray-600 dark:text-gray-400">Tasks Processed:</span>
101
+ <span class="font-medium text-green-600 dark:text-green-400">${worker.tasks_processed || 0}</span>
102
+ </div>
103
+ <div class="flex justify-between">
104
+ <span class="text-gray-600 dark:text-gray-400">Uptime:</span>
105
+ <span class="font-medium text-purple-600 dark:text-purple-400">${uptime}</span>
106
+ </div>
107
+ </div>
108
+
109
+ <div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-600">
110
+ <div class="flex items-center justify-between">
111
+ <div class="flex items-center text-xs text-gray-500 dark:text-gray-400">
112
+ <span class="material-icons text-sm mr-1">schedule</span>
113
+ Started: ${worker.started_at ? new Date(worker.started_at).toLocaleString() : 'Unknown'}
114
+ </div>
115
+ <div class="flex space-x-1">
116
+ <button class="p-1 text-gray-400 hover:text-blue-600 transition-colors" title="View details">
117
+ <span class="material-icons text-sm">info</span>
118
+ </button>
119
+ <button class="p-1 text-gray-400 hover:text-red-600 transition-colors" title="Stop worker">
120
+ <span class="material-icons text-sm">stop</span>
121
+ </button>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ `;
127
+ });
128
+
129
+ html += '</div>';
130
+ container.innerHTML = html;
131
+ }
132
+
133
+ /**
134
+ * Calculate uptime from start time
135
+ */
136
+ calculateUptime(startTime) {
137
+ if (!startTime) return 'Unknown';
138
+
139
+ const start = new Date(startTime);
140
+ const now = new Date();
141
+ const diff = now - start;
142
+
143
+ const hours = Math.floor(diff / (1000 * 60 * 60));
144
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
145
+
146
+ if (hours > 0) {
147
+ return `${hours}h ${minutes}m`;
148
+ } else if (minutes > 0) {
149
+ return `${minutes}m`;
150
+ } else {
151
+ return 'Just started';
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Show error message
157
+ */
158
+ showError(message) {
159
+ const container = document.getElementById('workers-container');
160
+ if (container) {
161
+ container.innerHTML = `
162
+ <div class="text-center py-8">
163
+ <span class="material-icons text-4xl text-red-400 mb-4">error</span>
164
+ <p class="text-red-600 dark:text-red-400">${message}</p>
165
+ </div>
166
+ `;
167
+ }
168
+ }
169
+ }
@@ -0,0 +1,10 @@
1
+ """
2
+ Tasks package for Django CFG Tasks app.
3
+
4
+ This package contains Dramatiq actors for background task processing.
5
+ """
6
+
7
+ # Import all task modules to ensure actors are registered
8
+ from . import demo_tasks
9
+
10
+ __all__ = ['demo_tasks']