django-cfg 1.3.7__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 (246) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/admin/__init__.py +24 -8
  3. django_cfg/apps/accounts/admin/activity_admin.py +146 -0
  4. django_cfg/apps/accounts/admin/filters.py +98 -22
  5. django_cfg/apps/accounts/admin/group_admin.py +86 -0
  6. django_cfg/apps/accounts/admin/inlines.py +42 -13
  7. django_cfg/apps/accounts/admin/otp_admin.py +115 -0
  8. django_cfg/apps/accounts/admin/registration_admin.py +173 -0
  9. django_cfg/apps/accounts/admin/resources.py +123 -19
  10. django_cfg/apps/accounts/admin/twilio_admin.py +327 -0
  11. django_cfg/apps/accounts/admin/user_admin.py +362 -0
  12. django_cfg/apps/agents/admin/__init__.py +17 -4
  13. django_cfg/apps/agents/admin/execution_admin.py +204 -183
  14. django_cfg/apps/agents/admin/registry_admin.py +230 -255
  15. django_cfg/apps/agents/admin/toolsets_admin.py +274 -321
  16. django_cfg/apps/agents/core/__init__.py +1 -1
  17. django_cfg/apps/agents/core/django_agent.py +221 -0
  18. django_cfg/apps/agents/core/exceptions.py +14 -0
  19. django_cfg/apps/agents/core/orchestrator.py +18 -3
  20. django_cfg/apps/knowbase/admin/__init__.py +1 -1
  21. django_cfg/apps/knowbase/admin/archive_admin.py +352 -640
  22. django_cfg/apps/knowbase/admin/chat_admin.py +258 -192
  23. django_cfg/apps/knowbase/admin/document_admin.py +269 -262
  24. django_cfg/apps/knowbase/admin/external_data_admin.py +271 -489
  25. django_cfg/apps/knowbase/config/settings.py +21 -4
  26. django_cfg/apps/knowbase/views/chat_views.py +3 -0
  27. django_cfg/apps/leads/admin/__init__.py +3 -1
  28. django_cfg/apps/leads/admin/leads_admin.py +235 -35
  29. django_cfg/apps/maintenance/admin/__init__.py +2 -2
  30. django_cfg/apps/maintenance/admin/api_key_admin.py +125 -63
  31. django_cfg/apps/maintenance/admin/log_admin.py +143 -61
  32. django_cfg/apps/maintenance/admin/scheduled_admin.py +212 -301
  33. django_cfg/apps/maintenance/admin/site_admin.py +213 -352
  34. django_cfg/apps/newsletter/admin/__init__.py +29 -2
  35. django_cfg/apps/newsletter/admin/newsletter_admin.py +531 -193
  36. django_cfg/apps/payments/admin/__init__.py +18 -27
  37. django_cfg/apps/payments/admin/api_keys_admin.py +179 -546
  38. django_cfg/apps/payments/admin/balance_admin.py +166 -632
  39. django_cfg/apps/payments/admin/currencies_admin.py +235 -607
  40. django_cfg/apps/payments/admin/endpoint_groups_admin.py +127 -0
  41. django_cfg/apps/payments/admin/filters.py +83 -3
  42. django_cfg/apps/payments/admin/networks_admin.py +269 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +183 -460
  44. django_cfg/apps/payments/admin/subscriptions_admin.py +119 -636
  45. django_cfg/apps/payments/admin/tariffs_admin.py +248 -0
  46. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +153 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  48. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  49. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  50. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  51. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  52. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  53. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  54. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +43 -17
  55. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  56. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  57. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +109 -63
  58. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  59. django_cfg/apps/payments/config/__init__.py +14 -15
  60. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  61. django_cfg/apps/payments/config/helpers.py +8 -13
  62. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  63. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  64. django_cfg/apps/payments/middleware/api_access.py +32 -6
  65. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  66. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  67. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  68. django_cfg/apps/payments/models/balance.py +12 -0
  69. django_cfg/apps/payments/models/currencies.py +106 -32
  70. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  71. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  72. django_cfg/apps/payments/models/payments.py +94 -0
  73. django_cfg/apps/payments/services/core/base.py +4 -4
  74. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  75. django_cfg/apps/payments/services/core/payment_service.py +266 -39
  76. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  77. django_cfg/apps/payments/services/providers/base.py +303 -41
  78. django_cfg/apps/payments/services/providers/models/__init__.py +42 -0
  79. django_cfg/apps/payments/services/providers/models/base.py +145 -0
  80. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  81. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  82. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  83. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  84. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  85. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  86. django_cfg/apps/payments/services/providers/nowpayments/provider.py +557 -0
  87. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  88. django_cfg/apps/payments/services/providers/registry.py +9 -37
  89. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  90. django_cfg/apps/payments/services/types/requests.py +19 -7
  91. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  92. django_cfg/apps/payments/static/payments/js/api-client.js +29 -6
  93. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  94. django_cfg/apps/payments/static/payments/js/payment-form.js +98 -32
  95. django_cfg/apps/payments/tasks/__init__.py +39 -0
  96. django_cfg/apps/payments/tasks/types.py +73 -0
  97. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  98. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  99. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  100. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  101. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  102. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  103. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  104. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  105. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  106. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  107. django_cfg/apps/payments/urls.py +3 -2
  108. django_cfg/apps/payments/urls_admin.py +1 -1
  109. django_cfg/apps/payments/views/api/currencies.py +8 -5
  110. django_cfg/apps/payments/views/overview/services.py +2 -2
  111. django_cfg/apps/payments/views/serializers/currencies.py +22 -8
  112. django_cfg/apps/support/admin/__init__.py +10 -1
  113. django_cfg/apps/support/admin/support_admin.py +338 -141
  114. django_cfg/apps/tasks/admin/__init__.py +11 -0
  115. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  116. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  117. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  118. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  119. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  120. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  121. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  122. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  123. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  124. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  125. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  126. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  127. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  128. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  129. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  130. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  131. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  132. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  133. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  134. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  135. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  136. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  137. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  138. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  139. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  140. django_cfg/apps/tasks/urls.py +2 -2
  141. django_cfg/apps/tasks/urls_admin.py +2 -2
  142. django_cfg/apps/tasks/utils/__init__.py +1 -0
  143. django_cfg/apps/tasks/utils/simulator.py +356 -0
  144. django_cfg/apps/tasks/views/__init__.py +16 -0
  145. django_cfg/apps/tasks/views/api.py +569 -0
  146. django_cfg/apps/tasks/views/dashboard.py +58 -0
  147. django_cfg/config.py +1 -1
  148. django_cfg/core/config.py +10 -5
  149. django_cfg/core/generation.py +1 -1
  150. django_cfg/core/integration/__init__.py +21 -0
  151. django_cfg/management/commands/__init__.py +13 -1
  152. django_cfg/management/commands/migrate_all.py +9 -3
  153. django_cfg/management/commands/migrator.py +11 -6
  154. django_cfg/management/commands/rundramatiq.py +3 -2
  155. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  156. django_cfg/middleware/__init__.py +0 -2
  157. django_cfg/models/api_keys.py +115 -0
  158. django_cfg/models/constance.py +0 -11
  159. django_cfg/models/payments.py +137 -3
  160. django_cfg/modules/django_admin/__init__.py +64 -0
  161. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  162. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  163. django_cfg/modules/django_admin/decorators/display.py +106 -0
  164. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  165. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  166. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  167. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  168. django_cfg/modules/django_admin/models/__init__.py +20 -0
  169. django_cfg/modules/django_admin/models/action_models.py +33 -0
  170. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  171. django_cfg/modules/django_admin/models/base.py +26 -0
  172. django_cfg/modules/django_admin/models/display_models.py +31 -0
  173. django_cfg/modules/django_admin/utils/badges.py +159 -0
  174. django_cfg/modules/django_admin/utils/displays.py +247 -0
  175. django_cfg/modules/django_currency/__init__.py +2 -2
  176. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  177. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  178. django_cfg/modules/django_currency/core/converter.py +12 -12
  179. django_cfg/modules/django_currency/database/__init__.py +2 -2
  180. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  181. django_cfg/modules/django_llm/llm/client.py +10 -2
  182. django_cfg/modules/django_tasks.py +54 -21
  183. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  184. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  185. django_cfg/modules/django_unfold/dashboard.py +14 -13
  186. django_cfg/modules/django_unfold/models/config.py +1 -1
  187. django_cfg/registry/core.py +7 -9
  188. django_cfg/registry/third_party.py +2 -2
  189. django_cfg/template_archive/django_sample.zip +0 -0
  190. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -1
  191. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/RECORD +198 -160
  192. django_cfg/apps/accounts/admin/activity.py +0 -96
  193. django_cfg/apps/accounts/admin/group.py +0 -17
  194. django_cfg/apps/accounts/admin/otp.py +0 -59
  195. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  196. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  197. django_cfg/apps/accounts/admin/user.py +0 -300
  198. django_cfg/apps/agents/core/agent.py +0 -281
  199. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  200. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  201. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  202. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  203. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  204. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  205. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  206. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  207. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  208. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  209. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  210. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  211. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  212. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  213. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  214. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  215. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  216. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  217. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  218. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  219. django_cfg/apps/payments/config/constance/fields.py +0 -69
  220. django_cfg/apps/payments/config/constance/settings.py +0 -160
  221. django_cfg/apps/payments/services/providers/nowpayments.py +0 -478
  222. django_cfg/apps/tasks/admin.py +0 -320
  223. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  224. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  225. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  226. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  227. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  228. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  229. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  230. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  231. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  232. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  233. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  234. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  235. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  236. django_cfg/apps/tasks/views.py +0 -461
  237. django_cfg/management/commands/auto_generate.py +0 -486
  238. django_cfg/middleware/static_nocache.py +0 -55
  239. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  240. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  241. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  242. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  243. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  244. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.3.7.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.3.7.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>