django-cfg 1.1.61__tar.gz → 1.1.63__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.61 → django_cfg-1.1.63}/PKG-INFO +145 -4
- {django_cfg-1.1.61 → django_cfg-1.1.63}/README.md +144 -3
- {django_cfg-1.1.61 → django_cfg-1.1.63}/pyproject.toml +1 -1
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/__init__.py +1 -1
- django_cfg-1.1.63/src/django_cfg/management/commands/rundramatiq.py +241 -0
- django_cfg-1.1.63/src/django_cfg/modules/django_tasks.py +373 -0
- django_cfg-1.1.63/src/django_cfg/modules/dramatiq_setup.py +16 -0
- django_cfg-1.1.61/src/django_cfg/management/commands/rundramatiq.py +0 -269
- django_cfg-1.1.61/src/django_cfg/modules/django_tasks.py +0 -747
- {django_cfg-1.1.61 → django_cfg-1.1.63}/.gitignore +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/LICENSE +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/MANIFEST.in +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/requirements-dev.txt +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/requirements-test.txt +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/requirements.txt +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/activity.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/filters.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/group.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/inlines.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/otp.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/registration_source.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/twilio_response.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/admin/user.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/apps.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/management/commands/test_otp.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/managers/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/managers/user_manager.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/migrations/0002_add_phone_otp_clean.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/migrations/0003_twilioresponse.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/migrations/0004_delete_twilioresponse.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/migrations/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/models.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/serializers/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/serializers/otp.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/serializers/profile.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/serializers/webhook.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/services/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/services/activity_service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/services/otp_service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/signals.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/templates/emails/base_email.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/templates/emails/base_email.txt +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/templates/emails/otp_email.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/templates/emails/otp_email.txt +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/templates/emails/welcome_email.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/templates/emails/welcome_email.txt +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/utils/notifications.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/views/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/views/otp.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/views/profile.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/accounts/views/webhook.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/api/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/api/commands/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/api/commands/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/api/commands/views.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/api/health/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/api/health/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/api/health/views.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/admin.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/apps.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/migrations/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/models.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/serializers.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/signals.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/tests.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/leads/views.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/admin.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/admin_filters.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/apps.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/management/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/management/commands/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/management/commands/test_newsletter.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/managers/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/managers/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/migrations/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/models.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/serializers.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/services/email_service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/signals.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/utils/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/views/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/views/campaigns.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/views/emails.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/views/newsletters.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/views/subscriptions.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/newsletter/views/tracking.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/admin.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/admin_filters.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/apps.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/managers/message_manager.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/managers/ticket_manager.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/migrations/0002_alter_message_ticket.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/migrations/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/models.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/serializers.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/signals.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/templates/support/chat/access_denied.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/templates/support/chat/ticket_chat.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/utils/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/utils/support_email_service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/views/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/views/admin.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/views/api.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/support/views/chat.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/admin.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/apps.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/serializers.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/static/tasks/js/api.js +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/static/tasks/js/modals.js +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/static/tasks/js/theme.js +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/base.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/status_cards.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/tasks/views.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps/urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/apps.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/archive/django_sample.zip +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/cli/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/cli/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/cli/commands/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/cli/commands/create_project.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/cli/commands/info.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/cli/main.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/cli/utils.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/core/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/core/config.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/core/environment.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/core/generation.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/core/validation.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/exceptions.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/integration.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/check_settings.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/clear_constance.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/create_token.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/generate.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/list_urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/migrator.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/runserver_ngrok.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/script.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/show_config.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/show_urls.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/superuser.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/task_clear.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/task_status.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/test_email.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/test_telegram.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/test_twilio.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/tree.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/management/commands/validate_config.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/middleware/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/middleware/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/middleware/user_activity.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/cache.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/constance.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/database.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/drf.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/jwt.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/limits.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/ngrok.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/revolution.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/services.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/tasks.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/models/unfold.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/base.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_currency/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_currency/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_currency/cache.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_currency/converter.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_currency/service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_email.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/example.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/llm/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/llm/cache.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/llm/client.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/llm/costs.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/llm/extractor.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/llm/models_cache.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/llm/tokenizer.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/translator/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/translator/cache.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_llm/translator/translator.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_logger.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_ngrok.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_telegram.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/README.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/exceptions.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/models.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/sendgrid_service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/simple_service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/templates/guide.md +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/templates/sendgrid_test_data.json +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/django_twilio/twilio_service.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/logger.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/unfold/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/unfold/callbacks.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/unfold/dashboard.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/unfold/models.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/unfold/system_monitor.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/modules/unfold/tailwind.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/routers.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/index.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/layouts/dashboard_with_tabs.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/activity_tracker.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/charts_section.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/django_commands.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/quick_actions.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/recent_activity.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/recent_users_table.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/stats_cards.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/stats_tiles.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/system_health.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/system_metrics.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/components/user_permissions.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/tabs/app_stats_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/tabs/commands_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/tabs/overview_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/tabs/stats_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/tabs/users_tab.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/admin/snippets/zones/zones_table.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/templates/emails/base_email.html +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/utils/__init__.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/utils/path_resolution.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/src/django_cfg/utils/smart_defaults.py +0 -0
- {django_cfg-1.1.61 → django_cfg-1.1.63}/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.63
|
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
|
@@ -173,7 +173,7 @@ python manage.py runserver
|
|
173
173
|
- ✅ **Newsletter campaigns** with email tracking
|
174
174
|
- ✅ **Lead management** system with CRM integration
|
175
175
|
- ✅ **Multi-database routing** and connection pooling
|
176
|
-
- ✅ **Background task processing** with Dramatiq
|
176
|
+
- ✅ **Background task processing** with production-ready Dramatiq integration
|
177
177
|
- ✅ **Webhook testing** with built-in ngrok integration
|
178
178
|
- ✅ **Type-safe configuration** with full IDE support
|
179
179
|
|
@@ -413,8 +413,137 @@ Automatic dev/staging/production detection with appropriate defaults.
|
|
413
413
|
### 🌐 **Built-in Ngrok Integration**
|
414
414
|
Instant webhook testing with zero-config ngrok tunnels for development.
|
415
415
|
|
416
|
-
### 🔄 **Background Task Processing**
|
417
|
-
|
416
|
+
### 🔄 **Background Task Processing** %%PRIORITY:HIGH%%
|
417
|
+
|
418
|
+
Django-CFG includes a complete Dramatiq integration for reliable background job processing with automatic Redis configuration, worker management, and task monitoring.
|
419
|
+
|
420
|
+
#### Features
|
421
|
+
- **🚀 Zero Configuration** - Automatic Redis broker setup and queue management
|
422
|
+
- **⚡ Smart Worker Management** - Built-in `rundramatiq` command with Django settings integration
|
423
|
+
- **🔧 Type-Safe Task Definition** - Full IDE support for task creation and scheduling
|
424
|
+
- **📊 Task Monitoring** - Built-in commands for queue status and task management
|
425
|
+
- **🐳 Docker Ready** - Pre-configured Docker containers for production deployment
|
426
|
+
- **🛡️ Production Tested** - Battle-tested configuration used in production environments
|
427
|
+
|
428
|
+
#### Quick Setup
|
429
|
+
```python
|
430
|
+
from django_cfg import DjangoConfig
|
431
|
+
|
432
|
+
class MyConfig(DjangoConfig):
|
433
|
+
project_name: str = "My App"
|
434
|
+
|
435
|
+
# Dramatiq configuration (automatic)
|
436
|
+
redis_url: str = "redis://localhost:6379/2" # Separate DB for tasks
|
437
|
+
|
438
|
+
# Optional: Custom task settings
|
439
|
+
dramatiq: DramatiqConfig = DramatiqConfig(
|
440
|
+
processes=2, # Worker processes
|
441
|
+
threads=4, # Threads per process
|
442
|
+
redis_db=2, # Redis database for tasks
|
443
|
+
queues=["default", "high", "low"] # Available queues
|
444
|
+
)
|
445
|
+
|
446
|
+
config = MyConfig()
|
447
|
+
```
|
448
|
+
|
449
|
+
#### Task Definition
|
450
|
+
```python
|
451
|
+
import dramatiq
|
452
|
+
from django_cfg.modules.django_tasks import get_broker
|
453
|
+
|
454
|
+
# Tasks are automatically discovered from your apps
|
455
|
+
@dramatiq.actor(queue_name="high", max_retries=3)
|
456
|
+
def process_document(document_id: str) -> dict:
|
457
|
+
"""Process document asynchronously with full Django context."""
|
458
|
+
from myapp.models import Document
|
459
|
+
|
460
|
+
document = Document.objects.get(id=document_id)
|
461
|
+
# Your processing logic here
|
462
|
+
document.status = "processed"
|
463
|
+
document.save()
|
464
|
+
|
465
|
+
return {"status": "completed", "document_id": document_id}
|
466
|
+
|
467
|
+
# Queue the task
|
468
|
+
process_document.send(document_id="123")
|
469
|
+
```
|
470
|
+
|
471
|
+
#### Management Commands
|
472
|
+
```bash
|
473
|
+
# Start Dramatiq workers with Django settings
|
474
|
+
python manage.py rundramatiq --processes 4 --threads 8
|
475
|
+
|
476
|
+
# Monitor task queues and status
|
477
|
+
python manage.py task_status --queue high
|
478
|
+
|
479
|
+
# Clear specific queues
|
480
|
+
python manage.py task_clear --queue default
|
481
|
+
|
482
|
+
# Test task processing pipeline
|
483
|
+
python manage.py test_tasks
|
484
|
+
```
|
485
|
+
|
486
|
+
#### Docker Integration
|
487
|
+
```yaml
|
488
|
+
# docker-compose.yml (automatically generated)
|
489
|
+
services:
|
490
|
+
app-dramatiq:
|
491
|
+
build: .
|
492
|
+
command: ["python", "manage.py", "rundramatiq"]
|
493
|
+
environment:
|
494
|
+
- DRAMATIQ_PROCESSES=2
|
495
|
+
- DRAMATIQ_THREADS=4
|
496
|
+
- DRAMATIQ_QUEUES=default,high,low
|
497
|
+
depends_on:
|
498
|
+
- redis
|
499
|
+
- postgres
|
500
|
+
```
|
501
|
+
|
502
|
+
#### Production Features
|
503
|
+
- **🔄 Automatic Restart** - Workers restart on code changes in development
|
504
|
+
- **📈 Scaling** - Easy horizontal scaling with multiple worker containers
|
505
|
+
- **🛡️ Error Handling** - Built-in retry logic and dead letter queues
|
506
|
+
- **📊 Monitoring** - Integration with Django admin for task monitoring
|
507
|
+
- **⚡ Performance** - Optimized Redis configuration for high throughput
|
508
|
+
|
509
|
+
#### Real-World Example
|
510
|
+
```python
|
511
|
+
# Document processing pipeline
|
512
|
+
@dramatiq.actor(queue_name="knowledge", max_retries=2)
|
513
|
+
def process_document_async(document_id: str) -> dict:
|
514
|
+
"""Complete document processing with chunking and embeddings."""
|
515
|
+
try:
|
516
|
+
document = Document.objects.get(id=document_id)
|
517
|
+
|
518
|
+
# Step 1: Chunk document
|
519
|
+
chunks = create_document_chunks(document)
|
520
|
+
|
521
|
+
# Step 2: Generate embeddings
|
522
|
+
for chunk in chunks:
|
523
|
+
generate_embeddings.send(chunk.id)
|
524
|
+
|
525
|
+
# Step 3: Optimize database
|
526
|
+
optimize_document_embeddings.send(document_id)
|
527
|
+
|
528
|
+
return {"status": "completed", "chunks_created": len(chunks)}
|
529
|
+
|
530
|
+
except Exception as e:
|
531
|
+
logger.error(f"Document processing failed: {e}")
|
532
|
+
return {"status": "failed", "error": str(e)}
|
533
|
+
|
534
|
+
# Triggered automatically via Django signals
|
535
|
+
@receiver(post_save, sender=Document)
|
536
|
+
def queue_document_processing(sender, instance, created, **kwargs):
|
537
|
+
if created:
|
538
|
+
process_document_async.send(str(instance.id))
|
539
|
+
```
|
540
|
+
|
541
|
+
**Perfect for:**
|
542
|
+
- 📄 **Document Processing** - PDF parsing, text extraction, embeddings
|
543
|
+
- 📧 **Email Campaigns** - Bulk email sending with delivery tracking
|
544
|
+
- 🔄 **Data Synchronization** - API integrations and data imports
|
545
|
+
- 📊 **Report Generation** - Heavy computational tasks
|
546
|
+
- 🧹 **Cleanup Tasks** - Database maintenance and optimization
|
418
547
|
|
419
548
|
---
|
420
549
|
|
@@ -444,6 +573,7 @@ Django-CFG includes powerful management commands for development and operations:
|
|
444
573
|
| **`rundramatiq`** | Run Dramatiq background task workers | `python manage.py rundramatiq --processes 4` |
|
445
574
|
| **`task_status`** | Show Dramatiq task status and queues | `python manage.py task_status --queue high` |
|
446
575
|
| **`task_clear`** | Clear Dramatiq queues | `python manage.py task_clear --queue default` |
|
576
|
+
| **`test_tasks`** | Test Dramatiq task processing pipeline | `python manage.py test_tasks --document-id 123` |
|
447
577
|
| **`tree`** | Display Django project structure | `python manage.py tree --depth 3 --include-docs` |
|
448
578
|
| **`validate_config`** | Deep validation of all settings | `python manage.py validate_config --strict` |
|
449
579
|
|
@@ -955,6 +1085,17 @@ class ProductionConfig(DjangoConfig):
|
|
955
1085
|
enable_newsletter: bool = True # Email marketing, campaigns, tracking & analytics
|
956
1086
|
enable_leads: bool = True # Lead capture, CRM integration, source tracking
|
957
1087
|
|
1088
|
+
# === Background Task Processing ===
|
1089
|
+
redis_url: str = "redis://redis:6379/2" # Separate DB for Dramatiq tasks
|
1090
|
+
dramatiq: DramatiqConfig = DramatiqConfig(
|
1091
|
+
processes=4, # Production worker processes
|
1092
|
+
threads=8, # Threads per process for I/O bound tasks
|
1093
|
+
redis_db=2, # Dedicated Redis DB for task queues
|
1094
|
+
queues=["default", "high", "low", "knowledge", "email"], # Production queues
|
1095
|
+
max_retries=3, # Default retry attempts
|
1096
|
+
max_age=3600000, # 1 hour max task age (milliseconds)
|
1097
|
+
)
|
1098
|
+
|
958
1099
|
# === Multi-Zone API ===
|
959
1100
|
revolution: RevolutionConfig = RevolutionConfig(
|
960
1101
|
api_prefix="api/v2",
|
@@ -47,7 +47,7 @@ python manage.py runserver
|
|
47
47
|
- ✅ **Newsletter campaigns** with email tracking
|
48
48
|
- ✅ **Lead management** system with CRM integration
|
49
49
|
- ✅ **Multi-database routing** and connection pooling
|
50
|
-
- ✅ **Background task processing** with Dramatiq
|
50
|
+
- ✅ **Background task processing** with production-ready Dramatiq integration
|
51
51
|
- ✅ **Webhook testing** with built-in ngrok integration
|
52
52
|
- ✅ **Type-safe configuration** with full IDE support
|
53
53
|
|
@@ -287,8 +287,137 @@ Automatic dev/staging/production detection with appropriate defaults.
|
|
287
287
|
### 🌐 **Built-in Ngrok Integration**
|
288
288
|
Instant webhook testing with zero-config ngrok tunnels for development.
|
289
289
|
|
290
|
-
### 🔄 **Background Task Processing**
|
291
|
-
|
290
|
+
### 🔄 **Background Task Processing** %%PRIORITY:HIGH%%
|
291
|
+
|
292
|
+
Django-CFG includes a complete Dramatiq integration for reliable background job processing with automatic Redis configuration, worker management, and task monitoring.
|
293
|
+
|
294
|
+
#### Features
|
295
|
+
- **🚀 Zero Configuration** - Automatic Redis broker setup and queue management
|
296
|
+
- **⚡ Smart Worker Management** - Built-in `rundramatiq` command with Django settings integration
|
297
|
+
- **🔧 Type-Safe Task Definition** - Full IDE support for task creation and scheduling
|
298
|
+
- **📊 Task Monitoring** - Built-in commands for queue status and task management
|
299
|
+
- **🐳 Docker Ready** - Pre-configured Docker containers for production deployment
|
300
|
+
- **🛡️ Production Tested** - Battle-tested configuration used in production environments
|
301
|
+
|
302
|
+
#### Quick Setup
|
303
|
+
```python
|
304
|
+
from django_cfg import DjangoConfig
|
305
|
+
|
306
|
+
class MyConfig(DjangoConfig):
|
307
|
+
project_name: str = "My App"
|
308
|
+
|
309
|
+
# Dramatiq configuration (automatic)
|
310
|
+
redis_url: str = "redis://localhost:6379/2" # Separate DB for tasks
|
311
|
+
|
312
|
+
# Optional: Custom task settings
|
313
|
+
dramatiq: DramatiqConfig = DramatiqConfig(
|
314
|
+
processes=2, # Worker processes
|
315
|
+
threads=4, # Threads per process
|
316
|
+
redis_db=2, # Redis database for tasks
|
317
|
+
queues=["default", "high", "low"] # Available queues
|
318
|
+
)
|
319
|
+
|
320
|
+
config = MyConfig()
|
321
|
+
```
|
322
|
+
|
323
|
+
#### Task Definition
|
324
|
+
```python
|
325
|
+
import dramatiq
|
326
|
+
from django_cfg.modules.django_tasks import get_broker
|
327
|
+
|
328
|
+
# Tasks are automatically discovered from your apps
|
329
|
+
@dramatiq.actor(queue_name="high", max_retries=3)
|
330
|
+
def process_document(document_id: str) -> dict:
|
331
|
+
"""Process document asynchronously with full Django context."""
|
332
|
+
from myapp.models import Document
|
333
|
+
|
334
|
+
document = Document.objects.get(id=document_id)
|
335
|
+
# Your processing logic here
|
336
|
+
document.status = "processed"
|
337
|
+
document.save()
|
338
|
+
|
339
|
+
return {"status": "completed", "document_id": document_id}
|
340
|
+
|
341
|
+
# Queue the task
|
342
|
+
process_document.send(document_id="123")
|
343
|
+
```
|
344
|
+
|
345
|
+
#### Management Commands
|
346
|
+
```bash
|
347
|
+
# Start Dramatiq workers with Django settings
|
348
|
+
python manage.py rundramatiq --processes 4 --threads 8
|
349
|
+
|
350
|
+
# Monitor task queues and status
|
351
|
+
python manage.py task_status --queue high
|
352
|
+
|
353
|
+
# Clear specific queues
|
354
|
+
python manage.py task_clear --queue default
|
355
|
+
|
356
|
+
# Test task processing pipeline
|
357
|
+
python manage.py test_tasks
|
358
|
+
```
|
359
|
+
|
360
|
+
#### Docker Integration
|
361
|
+
```yaml
|
362
|
+
# docker-compose.yml (automatically generated)
|
363
|
+
services:
|
364
|
+
app-dramatiq:
|
365
|
+
build: .
|
366
|
+
command: ["python", "manage.py", "rundramatiq"]
|
367
|
+
environment:
|
368
|
+
- DRAMATIQ_PROCESSES=2
|
369
|
+
- DRAMATIQ_THREADS=4
|
370
|
+
- DRAMATIQ_QUEUES=default,high,low
|
371
|
+
depends_on:
|
372
|
+
- redis
|
373
|
+
- postgres
|
374
|
+
```
|
375
|
+
|
376
|
+
#### Production Features
|
377
|
+
- **🔄 Automatic Restart** - Workers restart on code changes in development
|
378
|
+
- **📈 Scaling** - Easy horizontal scaling with multiple worker containers
|
379
|
+
- **🛡️ Error Handling** - Built-in retry logic and dead letter queues
|
380
|
+
- **📊 Monitoring** - Integration with Django admin for task monitoring
|
381
|
+
- **⚡ Performance** - Optimized Redis configuration for high throughput
|
382
|
+
|
383
|
+
#### Real-World Example
|
384
|
+
```python
|
385
|
+
# Document processing pipeline
|
386
|
+
@dramatiq.actor(queue_name="knowledge", max_retries=2)
|
387
|
+
def process_document_async(document_id: str) -> dict:
|
388
|
+
"""Complete document processing with chunking and embeddings."""
|
389
|
+
try:
|
390
|
+
document = Document.objects.get(id=document_id)
|
391
|
+
|
392
|
+
# Step 1: Chunk document
|
393
|
+
chunks = create_document_chunks(document)
|
394
|
+
|
395
|
+
# Step 2: Generate embeddings
|
396
|
+
for chunk in chunks:
|
397
|
+
generate_embeddings.send(chunk.id)
|
398
|
+
|
399
|
+
# Step 3: Optimize database
|
400
|
+
optimize_document_embeddings.send(document_id)
|
401
|
+
|
402
|
+
return {"status": "completed", "chunks_created": len(chunks)}
|
403
|
+
|
404
|
+
except Exception as e:
|
405
|
+
logger.error(f"Document processing failed: {e}")
|
406
|
+
return {"status": "failed", "error": str(e)}
|
407
|
+
|
408
|
+
# Triggered automatically via Django signals
|
409
|
+
@receiver(post_save, sender=Document)
|
410
|
+
def queue_document_processing(sender, instance, created, **kwargs):
|
411
|
+
if created:
|
412
|
+
process_document_async.send(str(instance.id))
|
413
|
+
```
|
414
|
+
|
415
|
+
**Perfect for:**
|
416
|
+
- 📄 **Document Processing** - PDF parsing, text extraction, embeddings
|
417
|
+
- 📧 **Email Campaigns** - Bulk email sending with delivery tracking
|
418
|
+
- 🔄 **Data Synchronization** - API integrations and data imports
|
419
|
+
- 📊 **Report Generation** - Heavy computational tasks
|
420
|
+
- 🧹 **Cleanup Tasks** - Database maintenance and optimization
|
292
421
|
|
293
422
|
---
|
294
423
|
|
@@ -318,6 +447,7 @@ Django-CFG includes powerful management commands for development and operations:
|
|
318
447
|
| **`rundramatiq`** | Run Dramatiq background task workers | `python manage.py rundramatiq --processes 4` |
|
319
448
|
| **`task_status`** | Show Dramatiq task status and queues | `python manage.py task_status --queue high` |
|
320
449
|
| **`task_clear`** | Clear Dramatiq queues | `python manage.py task_clear --queue default` |
|
450
|
+
| **`test_tasks`** | Test Dramatiq task processing pipeline | `python manage.py test_tasks --document-id 123` |
|
321
451
|
| **`tree`** | Display Django project structure | `python manage.py tree --depth 3 --include-docs` |
|
322
452
|
| **`validate_config`** | Deep validation of all settings | `python manage.py validate_config --strict` |
|
323
453
|
|
@@ -829,6 +959,17 @@ class ProductionConfig(DjangoConfig):
|
|
829
959
|
enable_newsletter: bool = True # Email marketing, campaigns, tracking & analytics
|
830
960
|
enable_leads: bool = True # Lead capture, CRM integration, source tracking
|
831
961
|
|
962
|
+
# === Background Task Processing ===
|
963
|
+
redis_url: str = "redis://redis:6379/2" # Separate DB for Dramatiq tasks
|
964
|
+
dramatiq: DramatiqConfig = DramatiqConfig(
|
965
|
+
processes=4, # Production worker processes
|
966
|
+
threads=8, # Threads per process for I/O bound tasks
|
967
|
+
redis_db=2, # Dedicated Redis DB for task queues
|
968
|
+
queues=["default", "high", "low", "knowledge", "email"], # Production queues
|
969
|
+
max_retries=3, # Default retry attempts
|
970
|
+
max_age=3600000, # 1 hour max task age (milliseconds)
|
971
|
+
)
|
972
|
+
|
832
973
|
# === Multi-Zone API ===
|
833
974
|
revolution: RevolutionConfig = RevolutionConfig(
|
834
975
|
api_prefix="api/v2",
|
@@ -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.63"
|
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.63"
|
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
|