django-cfg 1.1.59__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.
- {django_cfg-1.1.59 → django_cfg-1.1.62}/PKG-INFO +1 -1
- {django_cfg-1.1.59 → django_cfg-1.1.62}/pyproject.toml +1 -1
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/__init__.py +1 -1
- django_cfg-1.1.62/src/django_cfg/management/commands/rundramatiq.py +241 -0
- django_cfg-1.1.62/src/django_cfg/modules/django_tasks.py +373 -0
- django_cfg-1.1.62/src/django_cfg/modules/dramatiq_setup.py +16 -0
- django_cfg-1.1.59/src/django_cfg/management/commands/rundramatiq.py +0 -244
- django_cfg-1.1.59/src/django_cfg/modules/django_tasks.py +0 -806
- {django_cfg-1.1.59 → django_cfg-1.1.62}/.gitignore +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/LICENSE +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/MANIFEST.in +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/requirements-dev.txt +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/requirements-test.txt +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/requirements.txt +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/activity.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/filters.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/group.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/inlines.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/otp.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/registration_source.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/twilio_response.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/admin/user.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/apps.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/management/commands/test_otp.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/managers/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/managers/user_manager.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0002_add_phone_otp_clean.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0003_twilioresponse.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/0004_delete_twilioresponse.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/migrations/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/models.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/otp.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/profile.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/serializers/webhook.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/services/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/services/activity_service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/services/otp_service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/signals.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/base_email.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/base_email.txt +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/otp_email.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/otp_email.txt +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/welcome_email.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/templates/emails/welcome_email.txt +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/utils/notifications.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/otp.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/profile.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/accounts/views/webhook.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/api/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/api/commands/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/api/commands/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/api/commands/views.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/api/health/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/api/health/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/api/health/views.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/admin.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/apps.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/migrations/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/models.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/serializers.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/signals.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/tests.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/leads/views.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/admin.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/admin_filters.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/apps.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/management/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/management/commands/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/management/commands/test_newsletter.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/managers/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/managers/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/migrations/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/models.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/serializers.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/services/email_service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/signals.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/utils/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/campaigns.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/emails.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/newsletters.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/subscriptions.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/newsletter/views/tracking.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/admin.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/admin_filters.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/apps.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/managers/message_manager.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/managers/ticket_manager.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/migrations/0002_alter_message_ticket.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/migrations/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/models.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/serializers.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/signals.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/templates/support/chat/access_denied.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/templates/support/chat/ticket_chat.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/utils/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/utils/support_email_service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/admin.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/api.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/support/views/chat.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/admin.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/apps.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/serializers.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/api.js +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/modals.js +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/static/tasks/js/theme.js +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/base.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/status_cards.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/tasks/views.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps/urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/apps.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/archive/django_sample.zip +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/cli/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/cli/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/cli/commands/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/cli/commands/create_project.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/cli/commands/info.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/cli/main.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/cli/utils.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/core/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/core/config.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/core/environment.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/core/generation.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/core/validation.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/exceptions.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/integration.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/check_settings.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/clear_constance.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/create_token.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/generate.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/list_urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/migrator.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/runserver_ngrok.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/script.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/show_config.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/show_urls.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/superuser.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/task_clear.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/task_status.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/test_email.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/test_telegram.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/test_twilio.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/tree.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/management/commands/validate_config.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/middleware/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/middleware/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/middleware/user_activity.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/cache.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/constance.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/database.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/drf.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/jwt.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/limits.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/ngrok.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/revolution.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/services.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/tasks.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/models/unfold.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/base.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/cache.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/converter.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_currency/service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_email.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/example.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/cache.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/client.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/costs.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/extractor.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/models_cache.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/llm/tokenizer.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/translator/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/translator/cache.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_llm/translator/translator.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_logger.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_ngrok.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_telegram.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/README.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/exceptions.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/models.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/sendgrid_service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/simple_service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/templates/guide.md +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/templates/sendgrid_test_data.json +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/django_twilio/twilio_service.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/logger.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/callbacks.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/dashboard.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/models.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/system_monitor.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/modules/unfold/tailwind.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/routers.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/index.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/layouts/dashboard_with_tabs.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/activity_tracker.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/charts_section.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/django_commands.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/quick_actions.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/recent_activity.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/recent_users_table.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/stats_cards.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/stats_tiles.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/system_health.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/system_metrics.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/components/user_permissions.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/app_stats_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/commands_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/overview_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/stats_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/tabs/users_tab.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/admin/snippets/zones/zones_table.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/templates/emails/base_email.html +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/utils/__init__.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/utils/path_resolution.py +0 -0
- {django_cfg-1.1.59 → django_cfg-1.1.62}/src/django_cfg/utils/smart_defaults.py +0 -0
- {django_cfg-1.1.59 → 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.
|
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.
|
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.
|
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()
|