django-cfg 1.3.5__py3-none-any.whl → 1.3.9__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 (252) 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 +258 -0
  43. django_cfg/apps/payments/admin/payments_admin.py +171 -461
  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 +105 -34
  47. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +12 -16
  48. django_cfg/apps/payments/admin_interface/views/__init__.py +2 -0
  49. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +13 -18
  50. django_cfg/apps/payments/management/commands/manage_currencies.py +236 -274
  51. django_cfg/apps/payments/management/commands/manage_providers.py +4 -1
  52. django_cfg/apps/payments/middleware/api_access.py +32 -6
  53. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +26 -0
  54. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +28 -0
  55. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +30 -0
  56. django_cfg/apps/payments/models/balance.py +12 -0
  57. django_cfg/apps/payments/models/currencies.py +106 -32
  58. django_cfg/apps/payments/models/managers/currency_managers.py +65 -0
  59. django_cfg/apps/payments/services/core/currency_service.py +35 -28
  60. django_cfg/apps/payments/services/core/payment_service.py +1 -1
  61. django_cfg/apps/payments/services/providers/__init__.py +3 -0
  62. django_cfg/apps/payments/services/providers/base.py +95 -39
  63. django_cfg/apps/payments/services/providers/models/__init__.py +40 -0
  64. django_cfg/apps/payments/services/providers/models/base.py +122 -0
  65. django_cfg/apps/payments/services/providers/models/providers.py +87 -0
  66. django_cfg/apps/payments/services/providers/models/universal.py +48 -0
  67. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +31 -0
  68. django_cfg/apps/payments/services/providers/nowpayments/config.py +70 -0
  69. django_cfg/apps/payments/services/providers/nowpayments/models.py +150 -0
  70. django_cfg/apps/payments/services/providers/nowpayments/parsers.py +879 -0
  71. django_cfg/apps/payments/services/providers/{nowpayments.py → nowpayments/provider.py} +240 -209
  72. django_cfg/apps/payments/services/providers/nowpayments/sync.py +196 -0
  73. django_cfg/apps/payments/services/providers/registry.py +4 -32
  74. django_cfg/apps/payments/services/providers/sync_service.py +277 -0
  75. django_cfg/apps/payments/static/payments/js/api-client.js +23 -5
  76. django_cfg/apps/payments/static/payments/js/payment-form.js +65 -8
  77. django_cfg/apps/payments/tasks/__init__.py +39 -0
  78. django_cfg/apps/payments/tasks/types.py +73 -0
  79. django_cfg/apps/payments/tasks/usage_tracking.py +308 -0
  80. django_cfg/apps/payments/templates/admin/payments/_components/dashboard_header.html +23 -0
  81. django_cfg/apps/payments/templates/admin/payments/_components/stats_card.html +25 -0
  82. django_cfg/apps/payments/templates/admin/payments/_components/stats_grid.html +16 -0
  83. django_cfg/apps/payments/templates/admin/payments/apikey/change_list.html +39 -0
  84. django_cfg/apps/payments/templates/admin/payments/balance/change_list.html +50 -0
  85. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +40 -0
  86. django_cfg/apps/payments/templates/admin/payments/payment/change_list.html +48 -0
  87. django_cfg/apps/payments/templates/admin/payments/subscription/change_list.html +48 -0
  88. django_cfg/apps/payments/urls_admin.py +1 -1
  89. django_cfg/apps/payments/views/api/currencies.py +5 -5
  90. django_cfg/apps/payments/views/overview/services.py +2 -2
  91. django_cfg/apps/payments/views/serializers/currencies.py +4 -3
  92. django_cfg/apps/support/admin/__init__.py +10 -1
  93. django_cfg/apps/support/admin/support_admin.py +338 -141
  94. django_cfg/apps/tasks/admin/__init__.py +11 -0
  95. django_cfg/apps/tasks/admin/tasks_admin.py +430 -0
  96. django_cfg/apps/urls.py +1 -2
  97. django_cfg/config.py +1 -1
  98. django_cfg/core/config.py +10 -5
  99. django_cfg/core/generation.py +1 -1
  100. django_cfg/management/commands/__init__.py +13 -1
  101. django_cfg/management/commands/app_agent_diagnose.py +470 -0
  102. django_cfg/management/commands/app_agent_generate.py +342 -0
  103. django_cfg/management/commands/app_agent_info.py +308 -0
  104. django_cfg/management/commands/migrate_all.py +9 -3
  105. django_cfg/management/commands/migrator.py +11 -6
  106. django_cfg/management/commands/rundramatiq.py +3 -2
  107. django_cfg/middleware/__init__.py +0 -2
  108. django_cfg/models/api_keys.py +115 -0
  109. django_cfg/modules/django_admin/__init__.py +64 -0
  110. django_cfg/modules/django_admin/decorators/__init__.py +13 -0
  111. django_cfg/modules/django_admin/decorators/actions.py +106 -0
  112. django_cfg/modules/django_admin/decorators/display.py +106 -0
  113. django_cfg/modules/django_admin/mixins/__init__.py +14 -0
  114. django_cfg/modules/django_admin/mixins/display_mixin.py +81 -0
  115. django_cfg/modules/django_admin/mixins/optimization_mixin.py +41 -0
  116. django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +202 -0
  117. django_cfg/modules/django_admin/models/__init__.py +20 -0
  118. django_cfg/modules/django_admin/models/action_models.py +33 -0
  119. django_cfg/modules/django_admin/models/badge_models.py +20 -0
  120. django_cfg/modules/django_admin/models/base.py +26 -0
  121. django_cfg/modules/django_admin/models/display_models.py +31 -0
  122. django_cfg/modules/django_admin/utils/badges.py +159 -0
  123. django_cfg/modules/django_admin/utils/displays.py +247 -0
  124. django_cfg/modules/django_app_agent/__init__.py +87 -0
  125. django_cfg/modules/django_app_agent/agents/__init__.py +40 -0
  126. django_cfg/modules/django_app_agent/agents/base/__init__.py +24 -0
  127. django_cfg/modules/django_app_agent/agents/base/agent.py +354 -0
  128. django_cfg/modules/django_app_agent/agents/base/context.py +236 -0
  129. django_cfg/modules/django_app_agent/agents/base/executor.py +430 -0
  130. django_cfg/modules/django_app_agent/agents/generation/__init__.py +12 -0
  131. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +15 -0
  132. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +147 -0
  133. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +99 -0
  134. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +32 -0
  135. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +290 -0
  136. django_cfg/modules/django_app_agent/agents/interfaces.py +376 -0
  137. django_cfg/modules/django_app_agent/core/__init__.py +33 -0
  138. django_cfg/modules/django_app_agent/core/config.py +300 -0
  139. django_cfg/modules/django_app_agent/core/exceptions.py +359 -0
  140. django_cfg/modules/django_app_agent/models/__init__.py +71 -0
  141. django_cfg/modules/django_app_agent/models/base.py +283 -0
  142. django_cfg/modules/django_app_agent/models/context.py +496 -0
  143. django_cfg/modules/django_app_agent/models/enums.py +481 -0
  144. django_cfg/modules/django_app_agent/models/requests.py +500 -0
  145. django_cfg/modules/django_app_agent/models/responses.py +585 -0
  146. django_cfg/modules/django_app_agent/pytest.ini +6 -0
  147. django_cfg/modules/django_app_agent/services/__init__.py +42 -0
  148. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +30 -0
  149. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +133 -0
  150. django_cfg/modules/django_app_agent/services/app_generator/context.py +40 -0
  151. django_cfg/modules/django_app_agent/services/app_generator/main.py +202 -0
  152. django_cfg/modules/django_app_agent/services/app_generator/structure.py +316 -0
  153. django_cfg/modules/django_app_agent/services/app_generator/validation.py +125 -0
  154. django_cfg/modules/django_app_agent/services/base.py +437 -0
  155. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +34 -0
  156. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +141 -0
  157. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +276 -0
  158. django_cfg/modules/django_app_agent/services/context_builder/main.py +272 -0
  159. django_cfg/modules/django_app_agent/services/context_builder/models.py +40 -0
  160. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +85 -0
  161. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +31 -0
  162. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +311 -0
  163. django_cfg/modules/django_app_agent/services/project_scanner/main.py +221 -0
  164. django_cfg/modules/django_app_agent/services/project_scanner/models.py +59 -0
  165. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +94 -0
  166. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +28 -0
  167. django_cfg/modules/django_app_agent/services/questioning_service/main.py +273 -0
  168. django_cfg/modules/django_app_agent/services/questioning_service/models.py +111 -0
  169. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +251 -0
  170. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +347 -0
  171. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +356 -0
  172. django_cfg/modules/django_app_agent/services/report_service.py +332 -0
  173. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +18 -0
  174. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +236 -0
  175. django_cfg/modules/django_app_agent/services/template_manager/main.py +159 -0
  176. django_cfg/modules/django_app_agent/services/template_manager/models.py +36 -0
  177. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +100 -0
  178. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +105 -0
  179. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +31 -0
  180. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +44 -0
  181. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +81 -0
  182. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +107 -0
  183. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +139 -0
  184. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +91 -0
  185. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +195 -0
  186. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +35 -0
  187. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +211 -0
  188. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +200 -0
  189. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +25 -0
  190. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +333 -0
  191. django_cfg/modules/django_app_agent/services/validation_service/main.py +242 -0
  192. django_cfg/modules/django_app_agent/services/validation_service/models.py +66 -0
  193. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +352 -0
  194. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +272 -0
  195. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +203 -0
  196. django_cfg/modules/django_app_agent/ui/__init__.py +25 -0
  197. django_cfg/modules/django_app_agent/ui/cli.py +419 -0
  198. django_cfg/modules/django_app_agent/ui/rich_components.py +622 -0
  199. django_cfg/modules/django_app_agent/utils/__init__.py +38 -0
  200. django_cfg/modules/django_app_agent/utils/logging.py +360 -0
  201. django_cfg/modules/django_app_agent/utils/validation.py +417 -0
  202. django_cfg/modules/django_currency/__init__.py +2 -2
  203. django_cfg/modules/django_currency/clients/__init__.py +2 -2
  204. django_cfg/modules/django_currency/clients/hybrid_client.py +587 -0
  205. django_cfg/modules/django_currency/core/converter.py +12 -12
  206. django_cfg/modules/django_currency/database/__init__.py +2 -2
  207. django_cfg/modules/django_currency/database/database_loader.py +93 -42
  208. django_cfg/modules/django_llm/llm/client.py +10 -2
  209. django_cfg/modules/django_unfold/callbacks/actions.py +1 -1
  210. django_cfg/modules/django_unfold/callbacks/statistics.py +1 -1
  211. django_cfg/modules/django_unfold/dashboard.py +14 -13
  212. django_cfg/modules/django_unfold/models/config.py +1 -1
  213. django_cfg/registry/core.py +3 -0
  214. django_cfg/registry/third_party.py +2 -2
  215. django_cfg/template_archive/django_sample.zip +0 -0
  216. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/METADATA +2 -1
  217. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/RECORD +224 -118
  218. django_cfg/apps/accounts/admin/activity.py +0 -96
  219. django_cfg/apps/accounts/admin/group.py +0 -17
  220. django_cfg/apps/accounts/admin/otp.py +0 -59
  221. django_cfg/apps/accounts/admin/registration_source.py +0 -97
  222. django_cfg/apps/accounts/admin/twilio_response.py +0 -227
  223. django_cfg/apps/accounts/admin/user.py +0 -300
  224. django_cfg/apps/agents/core/agent.py +0 -281
  225. django_cfg/apps/payments/admin_interface/old/payments/base.html +0 -175
  226. django_cfg/apps/payments/admin_interface/old/payments/components/dev_tool_card.html +0 -125
  227. django_cfg/apps/payments/admin_interface/old/payments/components/loading_spinner.html +0 -16
  228. django_cfg/apps/payments/admin_interface/old/payments/components/ngrok_status_card.html +0 -113
  229. django_cfg/apps/payments/admin_interface/old/payments/components/notification.html +0 -27
  230. django_cfg/apps/payments/admin_interface/old/payments/components/provider_card.html +0 -86
  231. django_cfg/apps/payments/admin_interface/old/payments/components/status_card.html +0 -35
  232. django_cfg/apps/payments/admin_interface/old/payments/currency_converter.html +0 -382
  233. django_cfg/apps/payments/admin_interface/old/payments/payment_dashboard.html +0 -309
  234. django_cfg/apps/payments/admin_interface/old/payments/payment_form.html +0 -303
  235. django_cfg/apps/payments/admin_interface/old/payments/payment_list.html +0 -382
  236. django_cfg/apps/payments/admin_interface/old/payments/payment_status.html +0 -500
  237. django_cfg/apps/payments/admin_interface/old/payments/webhook_dashboard.html +0 -518
  238. django_cfg/apps/payments/admin_interface/old/static/payments/css/components.css +0 -619
  239. django_cfg/apps/payments/admin_interface/old/static/payments/css/dashboard.css +0 -188
  240. django_cfg/apps/payments/admin_interface/old/static/payments/js/components.js +0 -545
  241. django_cfg/apps/payments/admin_interface/old/static/payments/js/ngrok-status.js +0 -163
  242. django_cfg/apps/payments/admin_interface/old/static/payments/js/utils.js +0 -412
  243. django_cfg/apps/tasks/admin.py +0 -320
  244. django_cfg/middleware/static_nocache.py +0 -55
  245. django_cfg/modules/django_currency/clients/yahoo_client.py +0 -157
  246. /django_cfg/modules/{django_unfold → django_admin}/icons/README.md +0 -0
  247. /django_cfg/modules/{django_unfold → django_admin}/icons/__init__.py +0 -0
  248. /django_cfg/modules/{django_unfold → django_admin}/icons/constants.py +0 -0
  249. /django_cfg/modules/{django_unfold → django_admin}/icons/generate_icons.py +0 -0
  250. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/WHEEL +0 -0
  251. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/entry_points.txt +0 -0
  252. {django_cfg-1.3.5.dist-info → django_cfg-1.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,320 +0,0 @@
1
- """
2
- Admin interface for Dramatiq task management.
3
-
4
- Provides web interface for starting workers, checking queue status,
5
- and clearing queues with AJAX endpoints and interactive buttons.
6
- """
7
-
8
- import logging
9
- import subprocess
10
- import sys
11
- from typing import Dict, Any
12
-
13
- from django.db.models import Count
14
- from django.contrib import admin
15
- from django.contrib.admin.views.main import ChangeList
16
- from django.http import JsonResponse
17
- from django.urls import path
18
- from django.views.decorators.csrf import csrf_exempt
19
- from unfold.admin import ModelAdmin
20
-
21
- try:
22
- from django_dramatiq.models import Task
23
- from django_dramatiq.admin import TaskAdmin as BaseDramatiqTaskAdmin
24
- except ImportError:
25
- Task = None
26
- BaseDramatiqTaskAdmin = None
27
-
28
- from ...modules.django_tasks import DjangoTasks
29
-
30
-
31
- class TaskQueueChangeList(ChangeList):
32
- """Custom changelist for task queue management."""
33
-
34
- def __init__(self, *args, **kwargs):
35
- super().__init__(*args, **kwargs)
36
- self.tasks_service = DjangoTasks()
37
-
38
-
39
- class TaskQueueAdmin(ModelAdmin):
40
- """
41
- Enhanced admin for Dramatiq task management.
42
-
43
- Provides buttons for:
44
- - Starting/stopping workers
45
- - Checking queue status
46
- - Clearing queues
47
- - Viewing task statistics
48
- """
49
-
50
- def __init__(self, *args, **kwargs):
51
- super().__init__(*args, **kwargs)
52
- self.logger = logging.getLogger(__name__)
53
- self.tasks_service = DjangoTasks()
54
-
55
- def get_urls(self):
56
- """Add custom URLs for task management."""
57
- urls = super().get_urls()
58
- custom_urls = [
59
- path('queue-status/', csrf_exempt(self.queue_status_view), name='dramatiq_queue_status'),
60
- path('start-workers/', csrf_exempt(self.start_workers_view), name='dramatiq_start_workers'),
61
- path('clear-queues/', csrf_exempt(self.clear_queues_view), name='dramatiq_clear_queues'),
62
- path('task-stats/', csrf_exempt(self.task_stats_view), name='dramatiq_task_stats'),
63
- ]
64
- return custom_urls + urls
65
-
66
- def queue_status_view(self, request):
67
- """Get queue status and statistics."""
68
- if request.method != 'GET':
69
- return JsonResponse({'success': False, 'error': 'Method not allowed'}, status=405)
70
-
71
- try:
72
- # Get queue status using tasks service
73
- status_data = self._get_queue_status()
74
-
75
- return JsonResponse({
76
- 'success': True,
77
- 'data': status_data
78
- })
79
-
80
- except Exception as e:
81
- self.logger.error(f"Queue status check failed: {e}")
82
- return JsonResponse({
83
- 'success': False,
84
- 'error': str(e)
85
- }, status=500)
86
-
87
- def start_workers_view(self, request):
88
- """Start Dramatiq workers."""
89
- if request.method != 'POST':
90
- return JsonResponse({'success': False, 'error': 'Method not allowed'}, status=405)
91
-
92
- try:
93
- # Get parameters from request
94
- processes = int(request.POST.get('processes', 2))
95
- threads = int(request.POST.get('threads', 8))
96
- queues = request.POST.get('queues', '')
97
-
98
- # Validate parameters
99
- if processes < 1 or processes > 16:
100
- return JsonResponse({
101
- 'success': False,
102
- 'error': 'Processes must be between 1 and 16'
103
- }, status=400)
104
-
105
- if threads < 1 or threads > 32:
106
- return JsonResponse({
107
- 'success': False,
108
- 'error': 'Threads must be between 1 and 32'
109
- }, status=400)
110
-
111
- # Build command
112
- cmd = [sys.executable, 'manage.py', 'rundramatiq']
113
- cmd.extend(['--processes', str(processes)])
114
- cmd.extend(['--threads', str(threads)])
115
-
116
- if queues:
117
- cmd.extend(['--queues', queues])
118
-
119
- # Start workers in background
120
- process = subprocess.Popen(
121
- cmd,
122
- stdout=subprocess.PIPE,
123
- stderr=subprocess.PIPE,
124
- cwd=None # Use current working directory
125
- )
126
-
127
- return JsonResponse({
128
- 'success': True,
129
- 'message': f'Started {processes} worker processes with {threads} threads each',
130
- 'pid': process.pid,
131
- 'command': ' '.join(cmd)
132
- })
133
-
134
- except Exception as e:
135
- self.logger.error(f"Start workers failed: {e}")
136
- return JsonResponse({
137
- 'success': False,
138
- 'error': str(e)
139
- }, status=500)
140
-
141
- def clear_queues_view(self, request):
142
- """Clear Dramatiq queues."""
143
- if request.method != 'POST':
144
- return JsonResponse({'success': False, 'error': 'Method not allowed'}, status=405)
145
-
146
- try:
147
- # Get parameters
148
- queue_name = request.POST.get('queue', '')
149
- failed_only = request.POST.get('failed_only', 'false').lower() == 'true'
150
-
151
- # Build command
152
- cmd = [sys.executable, 'manage.py', 'task_clear', '--confirm']
153
-
154
- if queue_name:
155
- cmd.extend(['--queue', queue_name])
156
-
157
- if failed_only:
158
- cmd.append('--failed-only')
159
-
160
- # Execute command
161
- result = subprocess.run(
162
- cmd,
163
- capture_output=True,
164
- text=True,
165
- timeout=30
166
- )
167
-
168
- if result.returncode == 0:
169
- return JsonResponse({
170
- 'success': True,
171
- 'message': 'Queues cleared successfully',
172
- 'output': result.stdout
173
- })
174
- else:
175
- return JsonResponse({
176
- 'success': False,
177
- 'error': result.stderr or 'Clear command failed'
178
- }, status=500)
179
-
180
- except subprocess.TimeoutExpired:
181
- return JsonResponse({
182
- 'success': False,
183
- 'error': 'Clear operation timed out'
184
- }, status=500)
185
- except Exception as e:
186
- self.logger.error(f"Clear queues failed: {e}")
187
- return JsonResponse({
188
- 'success': False,
189
- 'error': str(e)
190
- }, status=500)
191
-
192
- def task_stats_view(self, request):
193
- """Get task statistics."""
194
- if request.method != 'GET':
195
- return JsonResponse({'success': False, 'error': 'Method not allowed'}, status=405)
196
-
197
- try:
198
- # Get task statistics using tasks service
199
- stats_data = self._get_task_statistics()
200
-
201
- return JsonResponse({
202
- 'success': True,
203
- 'data': stats_data
204
- })
205
-
206
- except Exception as e:
207
- self.logger.error(f"Task stats failed: {e}")
208
- return JsonResponse({
209
- 'success': False,
210
- 'error': str(e)
211
- }, status=500)
212
-
213
- def _get_queue_status(self) -> Dict[str, Any]:
214
- """Get current queue status."""
215
- try:
216
- # Use tasks service to get Redis connection
217
- redis_client = self.tasks_service.get_redis_client()
218
-
219
- if not redis_client:
220
- return {
221
- 'error': 'Redis connection not available',
222
- 'queues': {},
223
- 'workers': 0
224
- }
225
-
226
- # Get queue information
227
- queues_info = {}
228
- config = self.tasks_service.config
229
-
230
- if config and config.dramatiq and config.dramatiq.queues:
231
- for queue_name in config.dramatiq.queues:
232
- queue_key = f"dramatiq:default.DQ.{queue_name}"
233
- queue_length = redis_client.llen(queue_key)
234
-
235
- # Get failed queue length
236
- failed_key = f"dramatiq:default.DQ.{queue_name}.failed"
237
- failed_length = redis_client.llen(failed_key)
238
-
239
- queues_info[queue_name] = {
240
- 'pending': queue_length,
241
- 'failed': failed_length,
242
- 'total': queue_length + failed_length
243
- }
244
-
245
- # Get worker information (simplified)
246
- worker_keys = redis_client.keys("dramatiq:worker:*")
247
- active_workers = len(worker_keys) if worker_keys else 0
248
-
249
- return {
250
- 'queues': queues_info,
251
- 'workers': active_workers,
252
- 'redis_connected': True,
253
- 'timestamp': self.tasks_service._get_current_timestamp()
254
- }
255
-
256
- except Exception as e:
257
- self.logger.error(f"Queue status error: {e}")
258
- return {
259
- 'error': str(e),
260
- 'queues': {},
261
- 'workers': 0,
262
- 'redis_connected': False
263
- }
264
-
265
- def _get_task_statistics(self) -> Dict[str, Any]:
266
- """Get task execution statistics."""
267
- try:
268
- if not Task:
269
- return {'error': 'django_dramatiq not available'}
270
-
271
-
272
- stats = Task.tasks.aggregate(
273
- total=Count('id'),
274
- # Add more aggregations as needed
275
- )
276
-
277
- # Get recent tasks
278
- recent_tasks = list(
279
- Task.tasks.order_by('-created_at')[:10]
280
- .values('actor_name', 'status', 'created_at', 'updated_at')
281
- )
282
-
283
- return {
284
- 'statistics': stats,
285
- 'recent_tasks': recent_tasks,
286
- 'timestamp': self.tasks_service._get_current_timestamp()
287
- }
288
-
289
- except Exception as e:
290
- self.logger.error(f"Task statistics error: {e}")
291
- return {'error': str(e)}
292
-
293
-
294
- # Register the enhanced admin if django_dramatiq is available
295
- if Task and BaseDramatiqTaskAdmin:
296
- # Unregister the default admin
297
- admin.site.unregister(Task)
298
-
299
- # Register our enhanced admin
300
- @admin.register(Task)
301
- class EnhancedTaskAdmin(TaskQueueAdmin, BaseDramatiqTaskAdmin):
302
- """Enhanced Task admin with queue management buttons."""
303
-
304
- def get_changelist(self, request, **kwargs):
305
- """Use custom changelist."""
306
- return TaskQueueChangeList
307
-
308
- def changelist_view(self, request, extra_context=None):
309
- """Add extra context for queue management."""
310
- extra_context = extra_context or {}
311
-
312
- # Add queue status to context
313
- try:
314
- queue_status = self._get_queue_status()
315
- extra_context['queue_status'] = queue_status
316
- except Exception as e:
317
- self.logger.error(f"Failed to get queue status: {e}")
318
- extra_context['queue_status'] = {'error': str(e)}
319
-
320
- return super().changelist_view(request, extra_context)
@@ -1,55 +0,0 @@
1
- """
2
- Static files no-cache middleware for django-cfg.
3
-
4
- Automatically disables caching for static files in development environments
5
- to prevent browser caching issues during development.
6
- """
7
-
8
- from django.conf import settings
9
- from django_cfg.core.config import EnvironmentMode
10
-
11
-
12
- class StaticNoCacheMiddleware:
13
- """
14
- Middleware to disable caching for static files in development.
15
-
16
- This ensures that JavaScript and CSS files are always fresh during development,
17
- preventing browser caching issues when files are updated.
18
-
19
- Automatically detects development mode based on:
20
- - DEBUG setting
21
- - ENV_MODE environment variable
22
- """
23
-
24
- def __init__(self, get_response):
25
- self.get_response = get_response
26
-
27
- # Determine if we should disable caching
28
- self.should_disable_cache = self._should_disable_cache()
29
-
30
- def _should_disable_cache(self):
31
- """Determine if caching should be disabled based on environment."""
32
- # Always disable in DEBUG mode
33
- if settings.DEBUG:
34
- return True
35
-
36
- # Check ENV_MODE if available
37
- env_mode = getattr(settings, 'ENV_MODE', None)
38
- if env_mode == EnvironmentMode.DEVELOPMENT or env_mode == EnvironmentMode.TEST:
39
- return True
40
-
41
- return False
42
-
43
- def __call__(self, request):
44
- response = self.get_response(request)
45
-
46
- # Apply no-cache headers for static files in development
47
- if self.should_disable_cache and request.path.startswith('/static/'):
48
- response['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
49
- response['Pragma'] = 'no-cache'
50
- response['Expires'] = '0'
51
- # Add ETag removal to prevent conditional requests
52
- if 'ETag' in response:
53
- del response['ETag']
54
-
55
- return response
@@ -1,157 +0,0 @@
1
- import logging
2
- import requests
3
- import time
4
- from datetime import datetime
5
- from typing import Dict, Set, Optional
6
- from cachetools import TTLCache
7
-
8
- from ..core.models import Rate, YahooFinanceResponse
9
- from ..core.exceptions import RateFetchError
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
-
14
- class YahooFinanceClient:
15
- """Simple Yahoo Finance client without yfinance dependency."""
16
-
17
- BASE_URL = "https://query1.finance.yahoo.com/v8/finance/chart"
18
-
19
- def __init__(self, cache_ttl: int = 3600):
20
- """Initialize Yahoo Finance client with TTL cache."""
21
- self._rate_cache = TTLCache(maxsize=500, ttl=cache_ttl)
22
- self._session = requests.Session()
23
- self._session.headers.update({
24
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
25
- })
26
- self._last_request_time = 0
27
- self._rate_limit_delay = 1.0 # 1 second between requests
28
-
29
- def _get_yahoo_symbol(self, base: str, quote: str) -> str:
30
- """Convert currency pair to Yahoo Finance symbol format."""
31
- # Yahoo uses format like EURUSD=X for forex pairs
32
- return f"{base}{quote}=X"
33
-
34
- def fetch_rate(self, base: str, quote: str) -> Rate:
35
- """
36
- Fetch forex rate from Yahoo Finance with caching.
37
-
38
- Args:
39
- base: Base currency code (e.g., EUR)
40
- quote: Quote currency code (e.g., USD)
41
-
42
- Returns:
43
- Rate object with exchange rate data
44
-
45
- Raises:
46
- RateFetchError: If rate fetch fails
47
- """
48
- base = base.upper()
49
- quote = quote.upper()
50
- cache_key = f"{base}_{quote}"
51
-
52
- # Try cache first
53
- if cache_key in self._rate_cache:
54
- logger.debug(f"Retrieved rate {base}/{quote} from Yahoo cache")
55
- return self._rate_cache[cache_key]
56
-
57
- symbol = self._get_yahoo_symbol(base, quote)
58
-
59
- # Rate limiting
60
- current_time = time.time()
61
- time_since_last_request = current_time - self._last_request_time
62
- if time_since_last_request < self._rate_limit_delay:
63
- sleep_time = self._rate_limit_delay - time_since_last_request
64
- logger.debug(f"Rate limiting: sleeping for {sleep_time:.2f}s")
65
- time.sleep(sleep_time)
66
-
67
- try:
68
- response = self._session.get(f"{self.BASE_URL}/{symbol}")
69
- self._last_request_time = time.time()
70
- response.raise_for_status()
71
-
72
- raw_data = response.json()
73
-
74
- # Validate response using Pydantic model
75
- try:
76
- yahoo_response = YahooFinanceResponse(**raw_data)
77
- except Exception as e:
78
- raise RateFetchError(f"Invalid Yahoo Finance response format: {e}")
79
-
80
- if not yahoo_response.chart.result:
81
- raise RateFetchError(f"No data returned for {symbol}")
82
-
83
- meta = yahoo_response.chart.result[0].meta
84
- rate_value = meta.regularMarketPrice
85
- timestamp = datetime.fromtimestamp(meta.regularMarketTime)
86
-
87
- rate = Rate(
88
- source="yahoo",
89
- base_currency=base,
90
- quote_currency=quote,
91
- rate=float(rate_value),
92
- timestamp=timestamp
93
- )
94
-
95
- self._rate_cache[cache_key] = rate
96
- logger.info(f"Fetched rate {base}/{quote} = {rate_value} from Yahoo Finance")
97
- return rate
98
-
99
- except requests.exceptions.RequestException as e:
100
- logger.error(f"Failed to fetch rate from Yahoo Finance: {e}")
101
- raise RateFetchError(f"Yahoo Finance API error: {e}")
102
- except (KeyError, TypeError, ValueError) as e:
103
- logger.error(f"Failed to parse Yahoo Finance response: {e}")
104
- raise RateFetchError(f"Invalid response format: {e}")
105
- except Exception as e:
106
- logger.error(f"Unexpected error fetching from Yahoo Finance: {e}")
107
- raise RateFetchError(f"Yahoo Finance fetch failed: {e}")
108
-
109
- def supports_pair(self, base: str, quote: str) -> bool:
110
- """
111
- Check if Yahoo Finance supports the given currency pair.
112
-
113
- Yahoo Finance primarily supports major forex pairs.
114
- """
115
- base = base.upper()
116
- quote = quote.upper()
117
-
118
- # Major currencies supported by Yahoo Finance
119
- major_currencies = {
120
- 'USD', 'EUR', 'GBP', 'JPY', 'CHF', 'CAD', 'AUD', 'NZD',
121
- 'SEK', 'NOK', 'DKK', 'PLN', 'CZK', 'HUF', 'RUB', 'CNY',
122
- 'INR', 'KRW', 'SGD', 'HKD', 'THB', 'MXN', 'BRL', 'ZAR',
123
- 'TRY', 'ILS'
124
- }
125
-
126
- return base in major_currencies and quote in major_currencies
127
-
128
- def get_all_supported_currencies(self) -> Dict[str, str]:
129
- """Get all major currencies supported by Yahoo Finance."""
130
- return {
131
- 'USD': 'US Dollar',
132
- 'EUR': 'Euro',
133
- 'GBP': 'British Pound',
134
- 'JPY': 'Japanese Yen',
135
- 'CHF': 'Swiss Franc',
136
- 'CAD': 'Canadian Dollar',
137
- 'AUD': 'Australian Dollar',
138
- 'NZD': 'New Zealand Dollar',
139
- 'SEK': 'Swedish Krona',
140
- 'NOK': 'Norwegian Krone',
141
- 'DKK': 'Danish Krone',
142
- 'PLN': 'Polish Zloty',
143
- 'CZK': 'Czech Koruna',
144
- 'HUF': 'Hungarian Forint',
145
- 'RUB': 'Russian Ruble',
146
- 'CNY': 'Chinese Yuan',
147
- 'INR': 'Indian Rupee',
148
- 'KRW': 'South Korean Won',
149
- 'SGD': 'Singapore Dollar',
150
- 'HKD': 'Hong Kong Dollar',
151
- 'THB': 'Thai Baht',
152
- 'MXN': 'Mexican Peso',
153
- 'BRL': 'Brazilian Real',
154
- 'ZAR': 'South African Rand',
155
- 'TRY': 'Turkish Lira',
156
- 'ILS': 'Israeli Shekel'
157
- }