django-cfg 1.1.61__tar.gz → 1.1.62__tar.gz

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 (265) hide show
  1. {django_cfg-1.1.61 → django_cfg-1.1.62}/PKG-INFO +1 -1
  2. {django_cfg-1.1.61 → django_cfg-1.1.62}/pyproject.toml +1 -1
  3. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/__init__.py +1 -1
  4. django_cfg-1.1.62/src/django_cfg/management/commands/rundramatiq.py +241 -0
  5. django_cfg-1.1.62/src/django_cfg/modules/django_tasks.py +373 -0
  6. django_cfg-1.1.62/src/django_cfg/modules/dramatiq_setup.py +16 -0
  7. django_cfg-1.1.61/src/django_cfg/management/commands/rundramatiq.py +0 -269
  8. django_cfg-1.1.61/src/django_cfg/modules/django_tasks.py +0 -747
  9. {django_cfg-1.1.61 → django_cfg-1.1.62}/.gitignore +0 -0
  10. {django_cfg-1.1.61 → django_cfg-1.1.62}/LICENSE +0 -0
  11. {django_cfg-1.1.61 → django_cfg-1.1.62}/MANIFEST.in +0 -0
  12. {django_cfg-1.1.61 → django_cfg-1.1.62}/README.md +0 -0
  13. {django_cfg-1.1.61 → django_cfg-1.1.62}/requirements-dev.txt +0 -0
  14. {django_cfg-1.1.61 → django_cfg-1.1.62}/requirements-test.txt +0 -0
  15. {django_cfg-1.1.61 → django_cfg-1.1.62}/requirements.txt +0 -0
  16. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/__init__.py +0 -0
  17. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/README.md +0 -0
  18. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/__init__.py +0 -0
  19. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/__init__.py +0 -0
  20. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/activity.py +0 -0
  21. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/filters.py +0 -0
  22. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/group.py +0 -0
  23. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/inlines.py +0 -0
  24. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/otp.py +0 -0
  25. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/registration_source.py +0 -0
  26. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/twilio_response.py +0 -0
  27. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/user.py +0 -0
  28. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/apps.py +0 -0
  29. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/management/commands/test_otp.py +0 -0
  30. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/managers/__init__.py +0 -0
  31. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/managers/user_manager.py +0 -0
  32. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0001_initial.py +0 -0
  33. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0002_add_phone_otp_clean.py +0 -0
  34. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0003_twilioresponse.py +0 -0
  35. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0004_delete_twilioresponse.py +0 -0
  36. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/__init__.py +0 -0
  37. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/models.py +0 -0
  38. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/__init__.py +0 -0
  39. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/otp.py +0 -0
  40. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/profile.py +0 -0
  41. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/webhook.py +0 -0
  42. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/services/__init__.py +0 -0
  43. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/services/activity_service.py +0 -0
  44. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/services/otp_service.py +0 -0
  45. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/signals.py +0 -0
  46. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/base_email.html +0 -0
  47. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/base_email.txt +0 -0
  48. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/otp_email.html +0 -0
  49. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/otp_email.txt +0 -0
  50. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/welcome_email.html +0 -0
  51. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/welcome_email.txt +0 -0
  52. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/urls.py +0 -0
  53. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/utils/notifications.py +0 -0
  54. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/__init__.py +0 -0
  55. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/otp.py +0 -0
  56. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/profile.py +0 -0
  57. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/webhook.py +0 -0
  58. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/api/__init__.py +0 -0
  59. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/api/commands/__init__.py +0 -0
  60. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/api/commands/urls.py +0 -0
  61. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/api/commands/views.py +0 -0
  62. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/api/health/__init__.py +0 -0
  63. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/api/health/urls.py +0 -0
  64. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/api/health/views.py +0 -0
  65. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/README.md +0 -0
  66. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/__init__.py +0 -0
  67. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/admin.py +0 -0
  68. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/apps.py +0 -0
  69. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/migrations/0001_initial.py +0 -0
  70. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/migrations/__init__.py +0 -0
  71. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/models.py +0 -0
  72. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/serializers.py +0 -0
  73. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/signals.py +0 -0
  74. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/tests.py +0 -0
  75. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/urls.py +0 -0
  76. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/leads/views.py +0 -0
  77. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/README.md +0 -0
  78. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/__init__.py +0 -0
  79. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/admin.py +0 -0
  80. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/admin_filters.py +0 -0
  81. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/apps.py +0 -0
  82. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/management/__init__.py +0 -0
  83. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/management/commands/__init__.py +0 -0
  84. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/management/commands/test_newsletter.py +0 -0
  85. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/managers/README.md +0 -0
  86. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/managers/__init__.py +0 -0
  87. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/migrations/0001_initial.py +0 -0
  88. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/migrations/__init__.py +0 -0
  89. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/models.py +0 -0
  90. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/serializers.py +0 -0
  91. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/services/email_service.py +0 -0
  92. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/signals.py +0 -0
  93. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/urls.py +0 -0
  94. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/utils/__init__.py +0 -0
  95. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/__init__.py +0 -0
  96. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/campaigns.py +0 -0
  97. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/emails.py +0 -0
  98. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/newsletters.py +0 -0
  99. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/subscriptions.py +0 -0
  100. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/tracking.py +0 -0
  101. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/__init__.py +0 -0
  102. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/admin.py +0 -0
  103. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/admin_filters.py +0 -0
  104. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/apps.py +0 -0
  105. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/managers/message_manager.py +0 -0
  106. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/managers/ticket_manager.py +0 -0
  107. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/migrations/0001_initial.py +0 -0
  108. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/migrations/0002_alter_message_ticket.py +0 -0
  109. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/migrations/__init__.py +0 -0
  110. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/models.py +0 -0
  111. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/serializers.py +0 -0
  112. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/signals.py +0 -0
  113. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/templates/support/chat/access_denied.html +0 -0
  114. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/templates/support/chat/ticket_chat.html +0 -0
  115. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/urls.py +0 -0
  116. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/utils/__init__.py +0 -0
  117. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/utils/support_email_service.py +0 -0
  118. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/__init__.py +0 -0
  119. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/admin.py +0 -0
  120. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/api.py +0 -0
  121. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/chat.py +0 -0
  122. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/__init__.py +0 -0
  123. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/admin.py +0 -0
  124. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/apps.py +0 -0
  125. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/serializers.py +0 -0
  126. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -0
  127. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/api.js +0 -0
  128. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -0
  129. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/modals.js +0 -0
  130. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -0
  131. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -0
  132. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/theme.js +0 -0
  133. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/base.html +0 -0
  134. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -0
  135. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -0
  136. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -0
  137. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -0
  138. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/status_cards.html +0 -0
  139. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -0
  140. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -0
  141. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -0
  142. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -0
  143. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -0
  144. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/urls.py +0 -0
  145. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/views.py +0 -0
  146. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps/urls.py +0 -0
  147. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/apps.py +0 -0
  148. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/archive/django_sample.zip +0 -0
  149. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/cli/README.md +0 -0
  150. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/cli/__init__.py +0 -0
  151. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/cli/commands/__init__.py +0 -0
  152. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/cli/commands/create_project.py +0 -0
  153. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/cli/commands/info.py +0 -0
  154. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/cli/main.py +0 -0
  155. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/cli/utils.py +0 -0
  156. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/core/__init__.py +0 -0
  157. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/core/config.py +0 -0
  158. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/core/environment.py +0 -0
  159. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/core/generation.py +0 -0
  160. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/core/validation.py +0 -0
  161. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/exceptions.py +0 -0
  162. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/integration.py +0 -0
  163. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/__init__.py +0 -0
  164. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/__init__.py +0 -0
  165. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/check_settings.py +0 -0
  166. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/clear_constance.py +0 -0
  167. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/create_token.py +0 -0
  168. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/generate.py +0 -0
  169. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/list_urls.py +0 -0
  170. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/migrator.py +0 -0
  171. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/runserver_ngrok.py +0 -0
  172. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/script.py +0 -0
  173. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/show_config.py +0 -0
  174. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/show_urls.py +0 -0
  175. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/superuser.py +0 -0
  176. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/task_clear.py +0 -0
  177. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/task_status.py +0 -0
  178. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/test_email.py +0 -0
  179. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/test_telegram.py +0 -0
  180. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/test_twilio.py +0 -0
  181. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/tree.py +0 -0
  182. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/management/commands/validate_config.py +0 -0
  183. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/middleware/README.md +0 -0
  184. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/middleware/__init__.py +0 -0
  185. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/middleware/user_activity.py +0 -0
  186. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/__init__.py +0 -0
  187. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/cache.py +0 -0
  188. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/constance.py +0 -0
  189. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/database.py +0 -0
  190. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/drf.py +0 -0
  191. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/jwt.py +0 -0
  192. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/limits.py +0 -0
  193. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/ngrok.py +0 -0
  194. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/revolution.py +0 -0
  195. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/services.py +0 -0
  196. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/tasks.py +0 -0
  197. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/models/unfold.py +0 -0
  198. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/__init__.py +0 -0
  199. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/base.py +0 -0
  200. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/README.md +0 -0
  201. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/__init__.py +0 -0
  202. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/cache.py +0 -0
  203. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/converter.py +0 -0
  204. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/service.py +0 -0
  205. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_email.py +0 -0
  206. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/README.md +0 -0
  207. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/__init__.py +0 -0
  208. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/example.py +0 -0
  209. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/__init__.py +0 -0
  210. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/cache.py +0 -0
  211. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/client.py +0 -0
  212. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/costs.py +0 -0
  213. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/extractor.py +0 -0
  214. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/models_cache.py +0 -0
  215. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/tokenizer.py +0 -0
  216. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/translator/__init__.py +0 -0
  217. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/translator/cache.py +0 -0
  218. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/translator/translator.py +0 -0
  219. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_logger.py +0 -0
  220. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_ngrok.py +0 -0
  221. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_telegram.py +0 -0
  222. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/README.md +0 -0
  223. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/__init__.py +0 -0
  224. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/exceptions.py +0 -0
  225. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/models.py +0 -0
  226. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/sendgrid_service.py +0 -0
  227. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/service.py +0 -0
  228. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/simple_service.py +0 -0
  229. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/templates/guide.md +0 -0
  230. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html +0 -0
  231. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/templates/sendgrid_test_data.json +0 -0
  232. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/twilio_service.py +0 -0
  233. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/logger.py +0 -0
  234. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/__init__.py +0 -0
  235. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/callbacks.py +0 -0
  236. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/dashboard.py +0 -0
  237. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/models.py +0 -0
  238. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/system_monitor.py +0 -0
  239. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/tailwind.py +0 -0
  240. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/routers.py +0 -0
  241. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/__init__.py +0 -0
  242. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/index.html +0 -0
  243. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/layouts/dashboard_with_tabs.html +0 -0
  244. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/activity_tracker.html +0 -0
  245. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/charts_section.html +0 -0
  246. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/django_commands.html +0 -0
  247. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/quick_actions.html +0 -0
  248. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/recent_activity.html +0 -0
  249. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/recent_users_table.html +0 -0
  250. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/stats_cards.html +0 -0
  251. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/stats_tiles.html +0 -0
  252. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/system_health.html +0 -0
  253. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/system_metrics.html +0 -0
  254. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/user_permissions.html +0 -0
  255. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/app_stats_tab.html +0 -0
  256. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/commands_tab.html +0 -0
  257. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/overview_tab.html +0 -0
  258. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/stats_tab.html +0 -0
  259. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/users_tab.html +0 -0
  260. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/zones/zones_table.html +0 -0
  261. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/templates/emails/base_email.html +0 -0
  262. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/utils/__init__.py +0 -0
  263. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/utils/path_resolution.py +0 -0
  264. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/utils/smart_defaults.py +0 -0
  265. {django_cfg-1.1.61 → django_cfg-1.1.62}/src/django_cfg/version_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.1.61
3
+ Version: 1.1.62
4
4
  Summary: 🚀 Production-ready Django configuration framework with type-safe settings, smart automation, and modern developer experience
5
5
  Project-URL: Homepage, https://github.com/markolofsen/django-cfg
6
6
  Project-URL: Documentation, https://django-cfg.readthedocs.io
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.1.61"
7
+ version = "1.1.62"
8
8
  description = "🚀 Production-ready Django configuration framework with type-safe settings, smart automation, and modern developer experience"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -38,7 +38,7 @@ default_app_config = "django_cfg.apps.DjangoCfgConfig"
38
38
  from typing import TYPE_CHECKING
39
39
 
40
40
  # Version information
41
- __version__ = "1.1.60"
41
+ __version__ = "1.1.62"
42
42
  __author__ = "Unrealos Team"
43
43
  __email__ = "info@unrealos.com"
44
44
  __license__ = "MIT"
@@ -0,0 +1,241 @@
1
+ """
2
+ Django management command for running Dramatiq workers.
3
+
4
+ Based on django_dramatiq.management.commands.rundramatiq with Django-CFG integration.
5
+ Simple, clean, and working approach.
6
+ """
7
+
8
+ import argparse
9
+ import importlib
10
+ import multiprocessing
11
+ import os
12
+ import sys
13
+
14
+ from django.apps import apps
15
+ from django.conf import settings
16
+ from django.core.management.base import BaseCommand
17
+ from django.utils.module_loading import module_has_submodule
18
+
19
+ from django_cfg.modules.django_tasks import get_task_service
20
+
21
+
22
+ # Default values
23
+ NPROCS = multiprocessing.cpu_count()
24
+ NTHREADS = 8
25
+
26
+
27
+ class Command(BaseCommand):
28
+ help = "Run Dramatiq workers with Django-CFG configuration."
29
+
30
+ def add_arguments(self, parser):
31
+ parser.formatter_class = argparse.ArgumentDefaultsHelpFormatter
32
+
33
+ parser.add_argument(
34
+ "--processes", "-p",
35
+ default=NPROCS,
36
+ type=int,
37
+ help="The number of processes to run",
38
+ )
39
+ parser.add_argument(
40
+ "--threads", "-t",
41
+ default=NTHREADS,
42
+ type=int,
43
+ help="The number of threads per process to use",
44
+ )
45
+ parser.add_argument(
46
+ "--queues", "-Q",
47
+ nargs="*",
48
+ type=str,
49
+ help="Listen to a subset of queues, or all when empty",
50
+ )
51
+ parser.add_argument(
52
+ "--watch",
53
+ dest="watch_dir",
54
+ help="Reload workers when changes are detected in the given directory",
55
+ )
56
+ parser.add_argument(
57
+ "--pid-file",
58
+ type=str,
59
+ help="Write the PID of the master process to this file",
60
+ )
61
+ parser.add_argument(
62
+ "--log-file",
63
+ type=str,
64
+ help="Write all logs to a file, or stderr when empty",
65
+ )
66
+ parser.add_argument(
67
+ "--worker-shutdown-timeout",
68
+ type=int,
69
+ default=600000,
70
+ help="Timeout for worker shutdown, in milliseconds"
71
+ )
72
+ parser.add_argument(
73
+ "--dry-run",
74
+ action="store_true",
75
+ help="Show configuration without starting workers",
76
+ )
77
+
78
+ def handle(self, watch_dir, processes, threads, verbosity, queues,
79
+ pid_file, log_file, worker_shutdown_timeout, dry_run, **options):
80
+
81
+ # Get task service and validate
82
+ task_service = get_task_service()
83
+ if not task_service.is_enabled():
84
+ self.stdout.write(
85
+ self.style.ERROR("Task system is not enabled in Django-CFG configuration")
86
+ )
87
+ return
88
+
89
+ # Discover task modules
90
+ tasks_modules = self._discover_tasks_modules()
91
+
92
+ # Show configuration info
93
+ self.stdout.write(self.style.SUCCESS("Dramatiq Worker Configuration:"))
94
+ self.stdout.write(f"Processes: {processes}")
95
+ self.stdout.write(f"Threads: {threads}")
96
+ if queues:
97
+ self.stdout.write(f"Queues: {', '.join(queues)}")
98
+ else:
99
+ self.stdout.write("Queues: all")
100
+
101
+ self.stdout.write(f"\nDiscovered task modules:")
102
+ for module in tasks_modules:
103
+ self.stdout.write(f" - {module}")
104
+
105
+ # If dry run, show command and exit
106
+ if dry_run:
107
+ executable_name = "dramatiq"
108
+ process_args = [
109
+ executable_name,
110
+ "--processes", str(processes),
111
+ "--threads", str(threads),
112
+ "--worker-shutdown-timeout", str(worker_shutdown_timeout),
113
+ ]
114
+
115
+ if watch_dir:
116
+ process_args.extend(["--watch", watch_dir])
117
+
118
+ verbosity_args = ["-v"] * (verbosity - 1)
119
+ process_args.extend(verbosity_args)
120
+
121
+ if queues:
122
+ process_args.extend(["--queues", *queues])
123
+
124
+ if pid_file:
125
+ process_args.extend(["--pid-file", pid_file])
126
+
127
+ if log_file:
128
+ process_args.extend(["--log-file", log_file])
129
+
130
+ process_args.extend(tasks_modules)
131
+
132
+ self.stdout.write(f"\nCommand that would be executed:")
133
+ self.stdout.write(f' {" ".join(process_args)}')
134
+ return
135
+
136
+ # Show startup info
137
+ self.stdout.write(self.style.SUCCESS("\nStarting Dramatiq workers..."))
138
+
139
+ # Build dramatiq command
140
+ executable_name = "dramatiq"
141
+ executable_path = self._resolve_executable(executable_name)
142
+
143
+ # Build process arguments exactly like django_dramatiq
144
+ process_args = [
145
+ executable_name,
146
+ "--processes", str(processes),
147
+ "--threads", str(threads),
148
+ "--worker-shutdown-timeout", str(worker_shutdown_timeout),
149
+ ]
150
+
151
+ # Add watch directory if specified
152
+ if watch_dir:
153
+ process_args.extend(["--watch", watch_dir])
154
+
155
+ # Add verbosity
156
+ verbosity_args = ["-v"] * (verbosity - 1)
157
+ process_args.extend(verbosity_args)
158
+
159
+ # Add queues if specified
160
+ if queues:
161
+ process_args.extend(["--queues", *queues])
162
+
163
+ # Add PID file if specified
164
+ if pid_file:
165
+ process_args.extend(["--pid-file", pid_file])
166
+
167
+ # Add log file if specified
168
+ if log_file:
169
+ process_args.extend(["--log-file", log_file])
170
+
171
+ # Add task modules (broker module first, then discovered modules)
172
+ process_args.extend(tasks_modules)
173
+
174
+ self.stdout.write(f'Running dramatiq: "{" ".join(process_args)}"\n')
175
+
176
+ # Ensure DJANGO_SETTINGS_MODULE is set for worker processes
177
+ if not os.environ.get('DJANGO_SETTINGS_MODULE'):
178
+ if hasattr(settings, 'SETTINGS_MODULE'):
179
+ os.environ['DJANGO_SETTINGS_MODULE'] = settings.SETTINGS_MODULE
180
+ else:
181
+ # Try to detect from manage.py or current settings
182
+ import django
183
+ from django.conf import settings as django_settings
184
+ if hasattr(django_settings, '_wrapped') and hasattr(django_settings._wrapped, '__module__'):
185
+ module_name = django_settings._wrapped.__module__
186
+ os.environ['DJANGO_SETTINGS_MODULE'] = module_name
187
+ else:
188
+ self.stdout.write(
189
+ self.style.WARNING("Could not detect DJANGO_SETTINGS_MODULE")
190
+ )
191
+
192
+ # Use os.execvp like django_dramatiq to preserve environment
193
+ if sys.platform == "win32":
194
+ import subprocess
195
+ command = [executable_path] + process_args[1:]
196
+ sys.exit(subprocess.run(command))
197
+
198
+ os.execvp(executable_path, process_args)
199
+
200
+ def _discover_tasks_modules(self):
201
+ """Discover task modules like django_dramatiq does."""
202
+ # Always include our broker setup module first
203
+ tasks_modules = ["django_cfg.modules.dramatiq_setup"]
204
+
205
+ # Get task service for configuration
206
+ task_service = get_task_service()
207
+
208
+ # Try to get task modules from Django-CFG config
209
+ if task_service.config and task_service.config.auto_discover_tasks:
210
+ discovered = task_service.discover_tasks()
211
+ for module_name in discovered:
212
+ self.stdout.write(f"Discovered tasks module: '{module_name}'")
213
+ tasks_modules.append(module_name)
214
+
215
+ # Fallback: use django_dramatiq discovery logic
216
+ if len(tasks_modules) == 1: # Only broker module found
217
+ task_module_names = getattr(settings, "DRAMATIQ_AUTODISCOVER_MODULES", ("tasks",))
218
+
219
+ for app_config in apps.get_app_configs():
220
+ for task_module in task_module_names:
221
+ if module_has_submodule(app_config.module, task_module):
222
+ module_name = f"{app_config.name}.{task_module}"
223
+ try:
224
+ importlib.import_module(module_name)
225
+ self.stdout.write(f"Discovered tasks module: '{module_name}'")
226
+ tasks_modules.append(module_name)
227
+ except ImportError:
228
+ # Module exists but has import errors, skip it
229
+ pass
230
+
231
+ return tasks_modules
232
+
233
+ def _resolve_executable(self, exec_name):
234
+ """Resolve executable path like django_dramatiq does."""
235
+ bin_dir = os.path.dirname(sys.executable)
236
+ if bin_dir:
237
+ for d in [bin_dir, os.path.join(bin_dir, "Scripts")]:
238
+ exec_path = os.path.join(d, exec_name)
239
+ if os.path.isfile(exec_path):
240
+ return exec_path
241
+ return exec_name
@@ -0,0 +1,373 @@
1
+ """
2
+ Django-CFG Task Service Module.
3
+
4
+ Simplified and focused task service for Dramatiq integration.
5
+ Provides essential functionality without unnecessary complexity.
6
+ """
7
+
8
+ from typing import Optional, Dict, Any, List
9
+ import logging
10
+ from urllib.parse import urlparse
11
+
12
+ from django_cfg.modules.base import BaseModule
13
+ from django_cfg.models.tasks import TaskConfig, validate_task_config
14
+ from django_cfg.models.constance import ConstanceField
15
+
16
+ # Django imports (will be available when Django is configured)
17
+ try:
18
+ from django.conf import settings
19
+ from django.apps import apps
20
+ except ImportError:
21
+ settings = None
22
+ apps = None
23
+
24
+ # Optional imports
25
+ try:
26
+ import dramatiq
27
+ except ImportError:
28
+ dramatiq = None
29
+
30
+ try:
31
+ import redis
32
+ except ImportError:
33
+ redis = None
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ class DjangoTasks(BaseModule):
39
+ """
40
+ Simplified Django-CFG task service.
41
+
42
+ Focuses on essential functionality:
43
+ - Configuration management
44
+ - Task discovery
45
+ - Health checks
46
+ - Constance integration
47
+ """
48
+
49
+ def __init__(self):
50
+ super().__init__()
51
+ self._config: Optional[TaskConfig] = None
52
+ self._redis_url: Optional[str] = None
53
+
54
+ @property
55
+ def config(self) -> Optional[TaskConfig]:
56
+ """Get task configuration (lazy-loaded)."""
57
+ if self._config is None:
58
+ try:
59
+ # Get config from django-cfg
60
+ django_config = self.get_config()
61
+ if django_config and hasattr(django_config, 'tasks'):
62
+ self._config = django_config.tasks
63
+ logger.debug(f"Loaded TaskConfig: enabled={self._config.enabled if self._config else False}")
64
+ else:
65
+ # Fallback: try direct import
66
+ try:
67
+ from api.config import config as api_config
68
+ if hasattr(api_config, 'tasks') and api_config.tasks:
69
+ self._config = api_config.tasks
70
+ logger.debug(f"Loaded TaskConfig from api.config: enabled={self._config.enabled}")
71
+ except ImportError:
72
+ logger.debug("Could not import api.config")
73
+ except Exception as e:
74
+ logger.warning(f"Failed to get task config: {e}")
75
+
76
+ return self._config
77
+
78
+ def is_enabled(self) -> bool:
79
+ """Check if task system is enabled and properly configured."""
80
+ if not self.config or not self.config.enabled:
81
+ return False
82
+
83
+ # Check if required dependencies are available
84
+ if dramatiq is None:
85
+ logger.warning("Dramatiq not available")
86
+ return False
87
+
88
+ return True
89
+
90
+ def get_redis_url(self) -> Optional[str]:
91
+ """Get Redis URL from Django-CFG cache configuration."""
92
+ if self._redis_url is None:
93
+ try:
94
+ from django_cfg.core.config import get_current_config
95
+ django_config = get_current_config()
96
+
97
+ if not django_config:
98
+ try:
99
+ from api.config import config
100
+ django_config = config
101
+ except ImportError:
102
+ logger.warning("Could not import config from api.config")
103
+
104
+ if django_config and hasattr(django_config, 'cache_default') and django_config.cache_default:
105
+ cache_config = django_config.cache_default
106
+ if hasattr(cache_config, 'redis_url') and cache_config.redis_url:
107
+ self._redis_url = cache_config.redis_url
108
+ logger.debug(f"Got Redis URL: {self._redis_url}")
109
+ elif hasattr(cache_config, 'location') and cache_config.location:
110
+ self._redis_url = cache_config.location
111
+ logger.debug(f"Got Redis URL from location: {self._redis_url}")
112
+ except Exception as e:
113
+ logger.warning(f"Failed to get Redis URL: {e}")
114
+
115
+ return self._redis_url
116
+
117
+ def get_redis_client(self):
118
+ """Get Redis client instance."""
119
+ redis_url = self.get_redis_url()
120
+ if not redis_url or redis is None:
121
+ return None
122
+
123
+ try:
124
+ parsed = urlparse(redis_url)
125
+ return redis.Redis(
126
+ host=parsed.hostname or 'localhost',
127
+ port=parsed.port or 6379,
128
+ db=self.config.dramatiq.redis_db if self.config else 1,
129
+ password=parsed.password,
130
+ socket_timeout=5
131
+ )
132
+ except Exception as e:
133
+ logger.error(f"Failed to create Redis client: {e}")
134
+ return None
135
+
136
+ def _get_current_timestamp(self) -> str:
137
+ """Get current timestamp in ISO format."""
138
+ from datetime import datetime
139
+ return datetime.now().isoformat()
140
+
141
+ def check_redis_connection(self) -> bool:
142
+ """Check if Redis connection is available."""
143
+ redis_client = self.get_redis_client()
144
+ if not redis_client:
145
+ return False
146
+
147
+ try:
148
+ redis_client.ping()
149
+ return True
150
+ except Exception as e:
151
+ logger.error(f"Redis connection failed: {e}")
152
+ return False
153
+
154
+ def validate_configuration(self) -> bool:
155
+ """Validate complete task system configuration."""
156
+ if not self.config:
157
+ logger.error("Task configuration not available")
158
+ return False
159
+
160
+ redis_url = self.get_redis_url()
161
+ if not redis_url:
162
+ logger.error("Redis URL not configured")
163
+ return False
164
+
165
+ return validate_task_config(self.config, redis_url)
166
+
167
+ def discover_tasks(self) -> List[str]:
168
+ """Discover task modules in Django apps."""
169
+ if not self.config or not self.config.auto_discover_tasks:
170
+ return []
171
+
172
+ discovered = []
173
+
174
+ if apps is None:
175
+ logger.warning("Django apps not available")
176
+ return []
177
+
178
+ try:
179
+ for app_config in apps.get_app_configs():
180
+ for module_name in self.config.task_modules:
181
+ module_path = f"{app_config.name}.{module_name}"
182
+ try:
183
+ __import__(module_path)
184
+ discovered.append(module_path)
185
+ logger.debug(f"Discovered task module: {module_path}")
186
+ except ImportError:
187
+ # Module doesn't exist, which is fine
188
+ pass
189
+ except Exception as e:
190
+ logger.warning(f"Error importing task module {module_path}: {e}")
191
+ except Exception as e:
192
+ logger.error(f"Task discovery failed: {e}")
193
+
194
+ return discovered
195
+
196
+ def get_constance_fields(self) -> List[ConstanceField]:
197
+ """Get Constance fields for Dramatiq configuration."""
198
+ if not self.is_enabled():
199
+ return []
200
+
201
+ fields = [
202
+ ConstanceField(
203
+ name="DRAMATIQ_WORKER_PROCESSES",
204
+ default=self.config.dramatiq.processes if self.config else 2,
205
+ help_text="Number of worker processes for Dramatiq",
206
+ field_type="int",
207
+ group="Tasks",
208
+ ),
209
+ ConstanceField(
210
+ name="DRAMATIQ_WORKER_THREADS",
211
+ default=self.config.dramatiq.threads if self.config else 4,
212
+ help_text="Number of threads per worker process",
213
+ field_type="int",
214
+ group="Tasks",
215
+ ),
216
+ ConstanceField(
217
+ name="DRAMATIQ_MAX_RETRIES",
218
+ default=3,
219
+ help_text="Maximum number of retries for failed tasks",
220
+ field_type="int",
221
+ group="Tasks",
222
+ ),
223
+ ConstanceField(
224
+ name="DRAMATIQ_TASK_TIMEOUT",
225
+ default=600,
226
+ help_text="Task timeout in seconds (10 minutes default)",
227
+ field_type="int",
228
+ group="Tasks",
229
+ ),
230
+ ConstanceField(
231
+ name="DRAMATIQ_PROMETHEUS_ENABLED",
232
+ default=int(self.config.dramatiq.prometheus_enabled if self.config else False),
233
+ help_text="Enable Prometheus metrics for Dramatiq (0=disabled, 1=enabled)",
234
+ field_type="bool",
235
+ group="Tasks",
236
+ required=False,
237
+ ),
238
+ ]
239
+
240
+ logger.debug(f"Generated {len(fields)} Constance fields for Dramatiq")
241
+ return fields
242
+
243
+ def get_health_status(self) -> Dict[str, Any]:
244
+ """Get comprehensive health status of task system."""
245
+ status = {
246
+ "enabled": self.is_enabled(),
247
+ "redis_connection": False,
248
+ "configuration_valid": False,
249
+ "discovered_modules": [],
250
+ }
251
+
252
+ if self.is_enabled():
253
+ status["redis_connection"] = self.check_redis_connection()
254
+ status["configuration_valid"] = self.validate_configuration()
255
+ status["discovered_modules"] = self.discover_tasks()
256
+
257
+ return status
258
+
259
+
260
+ # === Service Factory ===
261
+
262
+ _task_service_instance: Optional[DjangoTasks] = None
263
+
264
+
265
+ def get_task_service() -> DjangoTasks:
266
+ """Get the global task service instance."""
267
+ global _task_service_instance
268
+
269
+ if _task_service_instance is None:
270
+ _task_service_instance = DjangoTasks()
271
+
272
+ return _task_service_instance
273
+
274
+
275
+ def reset_task_service():
276
+ """Reset the global task service instance (useful for testing)."""
277
+ global _task_service_instance
278
+ _task_service_instance = None
279
+
280
+
281
+ # === Utility Functions ===
282
+
283
+ def is_task_system_available() -> bool:
284
+ """Check if task system is available and properly configured."""
285
+ try:
286
+ service = get_task_service()
287
+ return service.is_enabled()
288
+ except Exception:
289
+ return False
290
+
291
+
292
+ def get_task_health() -> Dict[str, Any]:
293
+ """Get task system health status."""
294
+ try:
295
+ service = get_task_service()
296
+ return service.get_health_status()
297
+ except Exception as e:
298
+ return {
299
+ "enabled": False,
300
+ "error": str(e),
301
+ "redis_connection": False,
302
+ "configuration_valid": False,
303
+ }
304
+
305
+
306
+ def initialize_task_system():
307
+ """
308
+ Initialize the task system during Django app startup.
309
+ This function is called from Django AppConfig.ready() method.
310
+ """
311
+ try:
312
+ service = get_task_service()
313
+
314
+ # Force config reload to ensure we have fresh config
315
+ service._config = None
316
+ config = service.config
317
+
318
+ if config and config.enabled:
319
+ logger.info("🔧 Initializing Django-CFG task system...")
320
+
321
+ # Set up Dramatiq broker from Django settings
322
+ try:
323
+ import dramatiq
324
+ from django.conf import settings
325
+
326
+ # Django-dramatiq automatically configures the broker from DRAMATIQ_BROKER setting
327
+ if hasattr(settings, 'DRAMATIQ_BROKER'):
328
+ logger.debug("✅ Dramatiq broker configured from Django settings")
329
+ else:
330
+ logger.warning("DRAMATIQ_BROKER not found in Django settings")
331
+
332
+ except Exception as e:
333
+ logger.warning(f"Failed to configure Dramatiq: {e}")
334
+
335
+ logger.info("✅ Task system initialized successfully")
336
+ logger.info("💡 To start workers, run: python manage.py rundramatiq")
337
+ else:
338
+ logger.debug(f"Task system not enabled (config: {config}), skipping initialization")
339
+
340
+ except Exception as e:
341
+ logger.error(f"Failed to initialize task system: {e}")
342
+
343
+
344
+ def extend_constance_config_with_tasks():
345
+ """
346
+ Extend Constance configuration with Dramatiq task fields if tasks are enabled.
347
+ """
348
+ try:
349
+ service = get_task_service()
350
+ if not service.is_enabled():
351
+ logger.debug("Task system not enabled, skipping Constance extension")
352
+ return []
353
+
354
+ fields = service.get_constance_fields()
355
+ logger.info(f"🔧 Extended Constance with {len(fields)} task configuration fields")
356
+ return fields
357
+
358
+ except Exception as e:
359
+ logger.error(f"Failed to extend Constance config with tasks: {e}")
360
+ return []
361
+
362
+
363
+ # === Exports ===
364
+
365
+ __all__ = [
366
+ "DjangoTasks",
367
+ "get_task_service",
368
+ "reset_task_service",
369
+ "is_task_system_available",
370
+ "get_task_health",
371
+ "extend_constance_config_with_tasks",
372
+ "initialize_task_system",
373
+ ]
@@ -0,0 +1,16 @@
1
+ """
2
+ Dramatiq broker module for django-cfg CLI integration.
3
+
4
+ This module provides the broker instance required by Dramatiq CLI.
5
+ It's a thin wrapper around django_dramatiq.setup with broker export.
6
+
7
+ Usage:
8
+ dramatiq django_cfg.modules.dramatiq_setup [task_modules...]
9
+ """
10
+
11
+ # Import django_dramatiq setup (handles Django initialization)
12
+ import django_dramatiq.setup
13
+
14
+ # Re-export the broker for Dramatiq CLI
15
+ import dramatiq
16
+ broker = dramatiq.get_broker()