django-cfg 1.1.52__tar.gz → 1.1.54__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.52 → django_cfg-1.1.54}/PKG-INFO +6 -1
- {django_cfg-1.1.52 → django_cfg-1.1.54}/pyproject.toml +9 -2
- {django_cfg-1.1.52 → django_cfg-1.1.54}/requirements-dev.txt +5 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/requirements-test.txt +5 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/requirements.txt +5 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/__init__.py +3 -3
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/__init__.py +0 -2
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/filters.py +0 -54
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/otp.py +1 -12
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/managers/user_manager.py +0 -16
- django_cfg-1.1.54/src/django_cfg/apps/accounts/migrations/0004_delete_twilioresponse.py +16 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/models.py +0 -93
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/serializers/otp.py +2 -2
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/services/otp_service.py +25 -121
- django_cfg-1.1.54/src/django_cfg/apps/accounts/templates/emails/otp_email.html +94 -0
- django_cfg-1.1.54/src/django_cfg/apps/accounts/templates/emails/otp_email.txt +16 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/urls.py +1 -2
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/utils/notifications.py +32 -184
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/views/__init__.py +0 -3
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/views/otp.py +0 -8
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/api/commands/urls.py +0 -2
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/api/health/urls.py +0 -2
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/tasks/admin.py +5 -6
- django_cfg-1.1.54/src/django_cfg/apps/tasks/serializers.py +82 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/static/tasks/css/dashboard.css +269 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/static/tasks/js/api.js +144 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/static/tasks/js/dashboard.js +614 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/static/tasks/js/modals.js +452 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/static/tasks/js/notifications.js +144 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/static/tasks/js/task-monitor.js +454 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/static/tasks/js/theme.js +77 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/base.html +96 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/info_cards.html +85 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/management_actions.html +53 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +22 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +19 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/status_cards.html +50 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +27 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +103 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +32 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +29 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/templates/tasks/dashboard.html +29 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/urls.py +23 -0
- django_cfg-1.1.54/src/django_cfg/apps/tasks/views.py +448 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/archive/django_sample.zip +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/cli/README.md +1 -1
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/cli/commands/create_project.py +2 -2
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/cli/commands/info.py +1 -1
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/core/config.py +0 -6
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/core/generation.py +23 -1
- django_cfg-1.1.54/src/django_cfg/management/commands/clear_constance.py +200 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/rundramatiq.py +7 -5
- django_cfg-1.1.54/src/django_cfg/management/commands/show_urls.py +341 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/test_email.py +1 -1
- django_cfg-1.1.54/src/django_cfg/management/commands/test_twilio.py +101 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/constance.py +29 -23
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/base.py +19 -1
- django_cfg-1.1.54/src/django_cfg/modules/django_llm/README.md +388 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/__init__.py +5 -9
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/llm/client.py +197 -178
- django_cfg-1.1.54/src/django_cfg/modules/django_llm/llm/costs.py +188 -0
- django_cfg-1.1.54/src/django_cfg/modules/django_llm/llm/extractor.py +85 -0
- django_cfg-1.1.54/src/django_cfg/modules/django_llm/llm/tokenizer.py +83 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_tasks.py +282 -53
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/README.md +1 -1
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/__init__.py +37 -50
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/models.py +16 -34
- django_cfg-1.1.54/src/django_cfg/modules/django_twilio/service.py +942 -0
- django_cfg-1.1.54/src/django_cfg/modules/django_twilio/simple_service.py +290 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/unfold/dashboard.py +1 -45
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/utils/smart_defaults.py +17 -1
- django_cfg-1.1.52/src/django_cfg/apps/accounts/templates/emails/otp_email.html +0 -213
- django_cfg-1.1.52/src/django_cfg/apps/accounts/templates/emails/otp_email.txt +0 -29
- django_cfg-1.1.52/src/django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -449
- django_cfg-1.1.52/src/django_cfg/apps/tasks/urls.py +0 -21
- django_cfg-1.1.52/src/django_cfg/apps/tasks/views.py +0 -210
- django_cfg-1.1.52/src/django_cfg/management/commands/show_urls.py +0 -302
- django_cfg-1.1.52/src/django_cfg/management/commands/test_twilio.py +0 -614
- django_cfg-1.1.52/src/django_cfg/modules/django_llm/README.md +0 -524
- django_cfg-1.1.52/src/django_cfg/modules/django_llm/service.py +0 -423
- {django_cfg-1.1.52 → django_cfg-1.1.54}/.gitignore +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/LICENSE +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/MANIFEST.in +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/README.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/README.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/activity.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/group.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/inlines.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/registration_source.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/twilio_response.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/admin/user.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/apps.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/management/commands/test_otp.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/managers/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/migrations/0002_add_phone_otp_clean.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/migrations/0003_twilioresponse.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/migrations/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/serializers/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/serializers/profile.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/serializers/webhook.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/services/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/services/activity_service.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/signals.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/templates/emails/base_email.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/templates/emails/base_email.txt +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/templates/emails/welcome_email.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/templates/emails/welcome_email.txt +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/views/profile.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/views/webhook.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/api/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/api/commands/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/api/commands/views.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/api/health/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/api/health/views.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/README.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/admin.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/apps.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/migrations/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/models.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/serializers.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/signals.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/tests.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/urls.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/leads/views.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/README.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/admin.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/admin_filters.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/apps.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/management/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/management/commands/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/management/commands/test_newsletter.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/managers/README.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/managers/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/migrations/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/models.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/serializers.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/services/email_service.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/signals.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/urls.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/utils/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/views/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/views/campaigns.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/views/emails.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/views/newsletters.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/views/subscriptions.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/newsletter/views/tracking.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/admin.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/admin_filters.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/apps.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/managers/message_manager.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/managers/ticket_manager.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/migrations/0001_initial.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/migrations/0002_alter_message_ticket.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/migrations/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/models.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/serializers.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/signals.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/templates/support/chat/access_denied.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/templates/support/chat/ticket_chat.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/urls.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/utils/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/utils/support_email_service.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/views/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/views/admin.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/views/api.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/support/views/chat.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/tasks/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/tasks/apps.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/urls.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/cli/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/cli/commands/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/cli/main.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/cli/utils.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/core/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/core/environment.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/core/validation.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/exceptions.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/integration.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/check_settings.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/create_token.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/generate.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/list_urls.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/migrator.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/runserver_ngrok.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/script.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/show_config.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/superuser.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/task_clear.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/task_status.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/test_telegram.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/tree.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/management/commands/validate_config.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/middleware/README.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/middleware/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/middleware/user_activity.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/cache.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/database.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/drf.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/jwt.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/limits.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/ngrok.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/revolution.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/services.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/tasks.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/models/unfold.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_currency/README.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_currency/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_currency/cache.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_currency/converter.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_currency/service.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_email.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/example.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/llm/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/llm/cache.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/llm/models_cache.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/translator/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/translator/cache.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_llm/translator/translator.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_logger.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_ngrok.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_telegram.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/exceptions.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/sendgrid_service.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/templates/guide.md +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/templates/sendgrid_test_data.json +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/django_twilio/twilio_service.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/logger.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/unfold/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/unfold/callbacks.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/unfold/models.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/unfold/system_monitor.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/modules/unfold/tailwind.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/routers.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/index.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/layouts/dashboard_with_tabs.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/activity_tracker.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/charts_section.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/django_commands.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/quick_actions.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/recent_activity.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/recent_users_table.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/stats_cards.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/stats_tiles.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/system_health.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/system_metrics.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/components/user_permissions.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/tabs/app_stats_tab.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/tabs/commands_tab.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/tabs/overview_tab.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/tabs/stats_tab.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/tabs/users_tab.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/admin/snippets/zones/zones_table.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/templates/emails/base_email.html +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/utils/__init__.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/utils/path_resolution.py +0 -0
- {django_cfg-1.1.52 → django_cfg-1.1.54}/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.54
|
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
|
@@ -30,6 +30,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
30
|
Classifier: Topic :: System :: Systems Administration
|
31
31
|
Classifier: Typing :: Typed
|
32
32
|
Requires-Python: <4.0,>=3.12
|
33
|
+
Requires-Dist: beautifulsoup4<5.0.0,>=4.13.5
|
33
34
|
Requires-Dist: cachetools>=6.1.0
|
34
35
|
Requires-Dist: click>=8.2.0
|
35
36
|
Requires-Dist: colorlog>=6.9.0
|
@@ -42,6 +43,7 @@ Requires-Dist: django-cors-headers>=4.7.0
|
|
42
43
|
Requires-Dist: django-extensions>=4.1
|
43
44
|
Requires-Dist: django-filter>=25.0
|
44
45
|
Requires-Dist: django-json-widget>=2.0.3
|
46
|
+
Requires-Dist: django-ratelimit<5.0.0,>=4.1.0
|
45
47
|
Requires-Dist: django-redis>=6.0.0
|
46
48
|
Requires-Dist: django-revolution>=1.0.34
|
47
49
|
Requires-Dist: django-unfold>=0.64.0
|
@@ -53,8 +55,10 @@ Requires-Dist: drf-spectacular-sidecar>=2025.8.1
|
|
53
55
|
Requires-Dist: drf-spectacular>=0.28.0
|
54
56
|
Requires-Dist: hiredis>=2.0.0
|
55
57
|
Requires-Dist: loguru>=0.7.0
|
58
|
+
Requires-Dist: lxml<7.0.0,>=6.0.1
|
56
59
|
Requires-Dist: ngrok>=1.5.1; python_version >= '3.12'
|
57
60
|
Requires-Dist: openai<2.0.0,>=1.107.1
|
61
|
+
Requires-Dist: pgvector<0.5.0,>=0.4.1
|
58
62
|
Requires-Dist: psycopg[binary,pool]>=3.2.0
|
59
63
|
Requires-Dist: pydantic-yaml>=1.6.0
|
60
64
|
Requires-Dist: pydantic<3.0,>=2.11
|
@@ -68,6 +72,7 @@ Requires-Dist: requests<3.0.0,>=2.32.5
|
|
68
72
|
Requires-Dist: rich>=14.1.0
|
69
73
|
Requires-Dist: sendgrid<7.0.0,>=6.12.4
|
70
74
|
Requires-Dist: tiktoken<0.12.0,>=0.11.0
|
75
|
+
Requires-Dist: toml<0.11.0,>=0.10.2
|
71
76
|
Requires-Dist: twilio<10.0.0,>=9.8.0
|
72
77
|
Requires-Dist: whitenoise>=6.8.0
|
73
78
|
Provides-Extra: dev
|
@@ -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.54"
|
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"}
|
@@ -75,12 +75,13 @@ dependencies = [
|
|
75
75
|
"djangorestframework-simplejwt[token-blacklist]>=5.5.0",
|
76
76
|
"drf-nested-routers>=0.94.2",
|
77
77
|
"django-filter>=25.0",
|
78
|
+
"django-ratelimit (>=4.1.0,<5.0.0)",
|
78
79
|
"drf-spectacular>=0.28.0",
|
79
80
|
"drf-spectacular-sidecar>=2025.8.1",
|
80
81
|
"django-json-widget>=2.0.3",
|
81
82
|
"django-extensions>=4.1",
|
82
83
|
"django-constance>=4.3.2",
|
83
|
-
"django-revolution>=1.0.34",
|
84
|
+
"django-revolution>=1.0.34",
|
84
85
|
"django-unfold>=0.64.0",
|
85
86
|
"django-redis>=6.0.0",
|
86
87
|
"redis>=6.4.0",
|
@@ -97,6 +98,12 @@ dependencies = [
|
|
97
98
|
"openai (>=1.107.1,<2.0.0)",
|
98
99
|
"twilio (>=9.8.0,<10.0.0)",
|
99
100
|
"sendgrid (>=6.12.4,<7.0.0)",
|
101
|
+
|
102
|
+
# Knowbase
|
103
|
+
"beautifulsoup4 (>=4.13.5,<5.0.0)",
|
104
|
+
"lxml (>=6.0.1,<7.0.0)",
|
105
|
+
"pgvector (>=0.4.1,<0.5.0)",
|
106
|
+
"toml (>=0.10.2,<0.11.0)",
|
100
107
|
]
|
101
108
|
|
102
109
|
[project.optional-dependencies]
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# Do not edit manually!
|
4
4
|
|
5
5
|
PyYAML>=6.0
|
6
|
+
beautifulsoup4 (>=4.13.5,<5.0.0)
|
6
7
|
black>=23.0.0
|
7
8
|
build>=1.0.0
|
8
9
|
cachetools>=6.1.0
|
@@ -18,6 +19,7 @@ django-dramatiq>=0.14.0,<1.0
|
|
18
19
|
django-extensions>=4.1
|
19
20
|
django-filter>=25.0
|
20
21
|
django-json-widget>=2.0.3
|
22
|
+
django-ratelimit (>=4.1.0,<5.0.0)
|
21
23
|
django-redis>=6.0.0
|
22
24
|
django-revolution>=1.0.34
|
23
25
|
django-unfold>=0.64.0
|
@@ -34,12 +36,14 @@ flake8>=6.0.0
|
|
34
36
|
hiredis>=2.0.0
|
35
37
|
isort>=5.12.0
|
36
38
|
loguru>=0.7.0
|
39
|
+
lxml (>=6.0.1,<7.0.0)
|
37
40
|
mkdocs-material>=9.0.0
|
38
41
|
mkdocs>=1.5.0
|
39
42
|
mkdocstrings[python]>=0.22.0
|
40
43
|
mypy>=1.5.0
|
41
44
|
ngrok>=1.5.1; python_version>='3.12'
|
42
45
|
openai (>=1.107.1,<2.0.0)
|
46
|
+
pgvector (>=0.4.1,<0.5.0)
|
43
47
|
pre-commit>=3.0.0
|
44
48
|
psycopg[binary,pool]>=3.2.0
|
45
49
|
pyTelegramBotAPI>=4.28.0
|
@@ -60,6 +64,7 @@ rich>=13.0.0
|
|
60
64
|
rich>=14.1.0
|
61
65
|
sendgrid (>=6.12.4,<7.0.0)
|
62
66
|
tiktoken (>=0.11.0,<0.12.0)
|
67
|
+
toml (>=0.10.2,<0.11.0)
|
63
68
|
tomlkit>=0.13.3
|
64
69
|
twilio (>=9.8.0,<10.0.0)
|
65
70
|
twine>=4.0.0
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# Do not edit manually!
|
4
4
|
|
5
5
|
PyYAML>=6.0
|
6
|
+
beautifulsoup4 (>=4.13.5,<5.0.0)
|
6
7
|
cachetools>=6.1.0
|
7
8
|
click>=8.2.0
|
8
9
|
colorlog>=6.9.0
|
@@ -15,6 +16,7 @@ django-cors-headers>=4.7.0
|
|
15
16
|
django-extensions>=4.1
|
16
17
|
django-filter>=25.0
|
17
18
|
django-json-widget>=2.0.3
|
19
|
+
django-ratelimit (>=4.1.0,<5.0.0)
|
18
20
|
django-redis>=6.0.0
|
19
21
|
django-revolution>=1.0.34
|
20
22
|
django-unfold>=0.64.0
|
@@ -28,8 +30,10 @@ drf-spectacular>=0.28.0
|
|
28
30
|
factory-boy>=3.2.0
|
29
31
|
hiredis>=2.0.0
|
30
32
|
loguru>=0.7.0
|
33
|
+
lxml (>=6.0.1,<7.0.0)
|
31
34
|
ngrok>=1.5.1; python_version>='3.12'
|
32
35
|
openai (>=1.107.1,<2.0.0)
|
36
|
+
pgvector (>=0.4.1,<0.5.0)
|
33
37
|
psycopg[binary,pool]>=3.2.0
|
34
38
|
pyTelegramBotAPI>=4.28.0
|
35
39
|
pydantic-yaml>=1.6.0
|
@@ -47,5 +51,6 @@ requests (>=2.32.5,<3.0.0)
|
|
47
51
|
rich>=14.1.0
|
48
52
|
sendgrid (>=6.12.4,<7.0.0)
|
49
53
|
tiktoken (>=0.11.0,<0.12.0)
|
54
|
+
toml (>=0.10.2,<0.11.0)
|
50
55
|
twilio (>=9.8.0,<10.0.0)
|
51
56
|
whitenoise>=6.8.0
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# Do not edit manually!
|
4
4
|
|
5
5
|
PyYAML>=6.0
|
6
|
+
beautifulsoup4 (>=4.13.5,<5.0.0)
|
6
7
|
cachetools>=6.1.0
|
7
8
|
click>=8.2.0
|
8
9
|
colorlog>=6.9.0
|
@@ -15,6 +16,7 @@ django-cors-headers>=4.7.0
|
|
15
16
|
django-extensions>=4.1
|
16
17
|
django-filter>=25.0
|
17
18
|
django-json-widget>=2.0.3
|
19
|
+
django-ratelimit (>=4.1.0,<5.0.0)
|
18
20
|
django-redis>=6.0.0
|
19
21
|
django-revolution>=1.0.34
|
20
22
|
django-unfold>=0.64.0
|
@@ -26,8 +28,10 @@ drf-spectacular-sidecar>=2025.8.1
|
|
26
28
|
drf-spectacular>=0.28.0
|
27
29
|
hiredis>=2.0.0
|
28
30
|
loguru>=0.7.0
|
31
|
+
lxml (>=6.0.1,<7.0.0)
|
29
32
|
ngrok>=1.5.1; python_version>='3.12'
|
30
33
|
openai (>=1.107.1,<2.0.0)
|
34
|
+
pgvector (>=0.4.1,<0.5.0)
|
31
35
|
psycopg[binary,pool]>=3.2.0
|
32
36
|
pyTelegramBotAPI>=4.28.0
|
33
37
|
pydantic-yaml>=1.6.0
|
@@ -40,5 +44,6 @@ requests (>=2.32.5,<3.0.0)
|
|
40
44
|
rich>=14.1.0
|
41
45
|
sendgrid (>=6.12.4,<7.0.0)
|
42
46
|
tiktoken (>=0.11.0,<0.12.0)
|
47
|
+
toml (>=0.10.2,<0.11.0)
|
43
48
|
twilio (>=9.8.0,<10.0.0)
|
44
49
|
whitenoise>=6.8.0
|
@@ -38,9 +38,9 @@ default_app_config = "django_cfg.apps.DjangoCfgConfig"
|
|
38
38
|
from typing import TYPE_CHECKING
|
39
39
|
|
40
40
|
# Version information
|
41
|
-
__version__ = "1.1.
|
42
|
-
__author__ = "
|
43
|
-
__email__ = "info@
|
41
|
+
__version__ = "1.1.54"
|
42
|
+
__author__ = "Unrealos Team"
|
43
|
+
__email__ = "info@unrealos.com"
|
44
44
|
__license__ = "MIT"
|
45
45
|
|
46
46
|
# Core exports - only import when needed to avoid circular imports
|
@@ -7,7 +7,6 @@ from .otp import OTPSecretAdmin
|
|
7
7
|
from .registration_source import RegistrationSourceAdmin, UserRegistrationSourceAdmin
|
8
8
|
from .activity import UserActivityAdmin
|
9
9
|
from .group import GroupAdmin
|
10
|
-
from .twilio_response import TwilioResponseAdmin
|
11
10
|
|
12
11
|
__all__ = [
|
13
12
|
'CustomUserAdmin',
|
@@ -16,5 +15,4 @@ __all__ = [
|
|
16
15
|
'UserRegistrationSourceAdmin',
|
17
16
|
'UserActivityAdmin',
|
18
17
|
'GroupAdmin',
|
19
|
-
'TwilioResponseAdmin',
|
20
18
|
]
|
@@ -96,57 +96,3 @@ class ActivityTypeFilter(admin.SimpleListFilter):
|
|
96
96
|
elif self.value():
|
97
97
|
return queryset.filter(activity_type=self.value())
|
98
98
|
return queryset
|
99
|
-
|
100
|
-
|
101
|
-
class TwilioResponseStatusFilter(admin.SimpleListFilter):
|
102
|
-
title = "Response Status"
|
103
|
-
parameter_name = "twilio_status"
|
104
|
-
|
105
|
-
def lookups(self, request, model_admin):
|
106
|
-
return (
|
107
|
-
("successful", "Successful"),
|
108
|
-
("failed", "Failed"),
|
109
|
-
("pending", "Pending"),
|
110
|
-
("with_errors", "With Errors"),
|
111
|
-
("recent", "Recent (24h)"),
|
112
|
-
)
|
113
|
-
|
114
|
-
def queryset(self, request, queryset):
|
115
|
-
now = timezone.now()
|
116
|
-
if self.value() == "successful":
|
117
|
-
return queryset.filter(
|
118
|
-
status__in=['sent', 'delivered', 'approved'],
|
119
|
-
error_code__isnull=True
|
120
|
-
)
|
121
|
-
elif self.value() == "failed":
|
122
|
-
return queryset.filter(
|
123
|
-
status__in=['failed', 'undelivered', 'rejected']
|
124
|
-
)
|
125
|
-
elif self.value() == "pending":
|
126
|
-
return queryset.filter(status='pending')
|
127
|
-
elif self.value() == "with_errors":
|
128
|
-
return queryset.exclude(error_code__isnull=True)
|
129
|
-
elif self.value() == "recent":
|
130
|
-
return queryset.filter(created_at__gte=now - timedelta(hours=24))
|
131
|
-
return queryset
|
132
|
-
|
133
|
-
|
134
|
-
class TwilioResponseTypeFilter(admin.SimpleListFilter):
|
135
|
-
title = "Response Type"
|
136
|
-
parameter_name = "twilio_response_type"
|
137
|
-
|
138
|
-
def lookups(self, request, model_admin):
|
139
|
-
return (
|
140
|
-
("api_send", "API Send"),
|
141
|
-
("api_verify", "API Verify"),
|
142
|
-
("webhook_status", "Webhook Status"),
|
143
|
-
("webhook_delivery", "Webhook Delivery"),
|
144
|
-
("otp_related", "OTP Related"),
|
145
|
-
)
|
146
|
-
|
147
|
-
def queryset(self, request, queryset):
|
148
|
-
if self.value() == "otp_related":
|
149
|
-
return queryset.filter(otp_secret__isnull=False)
|
150
|
-
elif self.value():
|
151
|
-
return queryset.filter(response_type=self.value())
|
152
|
-
return queryset
|
@@ -8,18 +8,16 @@ from unfold.admin import ModelAdmin
|
|
8
8
|
|
9
9
|
from ..models import OTPSecret
|
10
10
|
from .filters import OTPStatusFilter
|
11
|
-
from .twilio_response import TwilioResponseInline
|
12
11
|
|
13
12
|
|
14
13
|
@admin.register(OTPSecret)
|
15
14
|
class OTPSecretAdmin(ModelAdmin):
|
16
|
-
list_display = ["recipient", "channel_type", "secret", "status", "
|
15
|
+
list_display = ["recipient", "channel_type", "secret", "status", "created", "expires"]
|
17
16
|
list_display_links = ["recipient", "secret"]
|
18
17
|
list_filter = [OTPStatusFilter, "channel_type", "is_used", "created_at"]
|
19
18
|
search_fields = ["recipient", "secret"]
|
20
19
|
readonly_fields = ["created_at", "expires_at"]
|
21
20
|
ordering = ["-created_at"]
|
22
|
-
inlines = [TwilioResponseInline]
|
23
21
|
|
24
22
|
fieldsets = (
|
25
23
|
(
|
@@ -59,12 +57,3 @@ class OTPSecretAdmin(ModelAdmin):
|
|
59
57
|
return naturaltime(obj.expires_at)
|
60
58
|
|
61
59
|
expires.short_description = "Expires"
|
62
|
-
|
63
|
-
def twilio_responses_count(self, obj):
|
64
|
-
"""Count of related Twilio responses."""
|
65
|
-
count = obj.twilio_responses.count()
|
66
|
-
if count == 0:
|
67
|
-
return "—"
|
68
|
-
return f"{count} response{'s' if count != 1 else ''}"
|
69
|
-
|
70
|
-
twilio_responses_count.short_description = "Twilio"
|
{django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/managers/user_manager.py
RENAMED
@@ -246,11 +246,6 @@ class UserManager(UserManager):
|
|
246
246
|
return user.first_name
|
247
247
|
elif user.last_name:
|
248
248
|
return user.last_name
|
249
|
-
|
250
|
-
# For phone users with temp email, use display_username instead
|
251
|
-
if user.email and user.email.startswith('phone_') and '@temp.' in user.email:
|
252
|
-
return self.get_display_username(user)
|
253
|
-
|
254
249
|
return user.email
|
255
250
|
|
256
251
|
def get_initials(self, user) -> str:
|
@@ -267,17 +262,6 @@ class UserManager(UserManager):
|
|
267
262
|
return user.first_name[0].upper()
|
268
263
|
elif user.last_name:
|
269
264
|
return user.last_name[0].upper()
|
270
|
-
|
271
|
-
# For phone users with temp email, use username initials instead
|
272
|
-
if user.email and user.email.startswith('phone_') and '@temp.' in user.email:
|
273
|
-
if user.username:
|
274
|
-
# Take first two characters of username
|
275
|
-
clean_username = user.username.replace("_", "").replace("-", "").replace(".", "")
|
276
|
-
if len(clean_username) >= 2:
|
277
|
-
return f"{clean_username[0]}{clean_username[1]}".upper()
|
278
|
-
elif len(clean_username) == 1:
|
279
|
-
return clean_username[0].upper()
|
280
|
-
|
281
265
|
return user.email[0].upper()
|
282
266
|
|
283
267
|
def get_display_username(self, user) -> str:
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Generated by Django 5.2.6 on 2025-09-16 18:57
|
2
|
+
|
3
|
+
from django.db import migrations
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('django_cfg_accounts', '0003_twilioresponse'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.DeleteModel(
|
14
|
+
name='TwilioResponse',
|
15
|
+
),
|
16
|
+
]
|
@@ -203,99 +203,6 @@ class OTPSecret(models.Model):
|
|
203
203
|
]
|
204
204
|
|
205
205
|
|
206
|
-
class TwilioResponse(models.Model):
|
207
|
-
"""
|
208
|
-
Store Twilio API responses and webhook events.
|
209
|
-
|
210
|
-
This model tracks all interactions with Twilio including:
|
211
|
-
- API responses from sending messages/OTP
|
212
|
-
- Webhook events for delivery status updates
|
213
|
-
- Error tracking and debugging information
|
214
|
-
"""
|
215
|
-
|
216
|
-
RESPONSE_TYPES = [
|
217
|
-
('api_send', 'API Send Request'),
|
218
|
-
('api_verify', 'API Verify Request'),
|
219
|
-
('webhook_status', 'Webhook Status Update'),
|
220
|
-
('webhook_delivery', 'Webhook Delivery Report'),
|
221
|
-
]
|
222
|
-
|
223
|
-
SERVICE_TYPES = [
|
224
|
-
('whatsapp', 'WhatsApp'),
|
225
|
-
('sms', 'SMS'),
|
226
|
-
('voice', 'Voice'),
|
227
|
-
('email', 'Email'),
|
228
|
-
('verify', 'Verify API'),
|
229
|
-
]
|
230
|
-
|
231
|
-
# Basic info
|
232
|
-
response_type = models.CharField(max_length=20, choices=RESPONSE_TYPES)
|
233
|
-
service_type = models.CharField(max_length=10, choices=SERVICE_TYPES)
|
234
|
-
|
235
|
-
# Twilio identifiers
|
236
|
-
message_sid = models.CharField(max_length=34, blank=True, help_text="Twilio Message SID")
|
237
|
-
verification_sid = models.CharField(max_length=34, blank=True, help_text="Twilio Verification SID")
|
238
|
-
|
239
|
-
# Request/Response data
|
240
|
-
request_data = models.JSONField(default=dict, help_text="Original request parameters")
|
241
|
-
response_data = models.JSONField(default=dict, help_text="Twilio API response")
|
242
|
-
|
243
|
-
# Status tracking
|
244
|
-
status = models.CharField(max_length=20, blank=True, help_text="Message/Verification status")
|
245
|
-
error_code = models.CharField(max_length=10, blank=True, help_text="Twilio error code")
|
246
|
-
error_message = models.TextField(blank=True, help_text="Error description")
|
247
|
-
|
248
|
-
# Recipient info
|
249
|
-
to_number = models.CharField(max_length=20, blank=True, help_text="Recipient phone/email")
|
250
|
-
from_number = models.CharField(max_length=20, blank=True, help_text="Sender phone/email")
|
251
|
-
|
252
|
-
# Pricing
|
253
|
-
price = models.DecimalField(max_digits=10, decimal_places=6, null=True, blank=True)
|
254
|
-
price_unit = models.CharField(max_length=3, blank=True, help_text="Currency code")
|
255
|
-
|
256
|
-
# Timestamps
|
257
|
-
created_at = models.DateTimeField(auto_now_add=True)
|
258
|
-
updated_at = models.DateTimeField(auto_now=True)
|
259
|
-
twilio_created_at = models.DateTimeField(null=True, blank=True, help_text="Timestamp from Twilio")
|
260
|
-
|
261
|
-
# Relations
|
262
|
-
otp_secret = models.ForeignKey(
|
263
|
-
'OTPSecret',
|
264
|
-
on_delete=models.SET_NULL,
|
265
|
-
null=True,
|
266
|
-
blank=True,
|
267
|
-
related_name='twilio_responses',
|
268
|
-
help_text="Related OTP if applicable"
|
269
|
-
)
|
270
|
-
|
271
|
-
class Meta:
|
272
|
-
app_label = 'django_cfg_accounts'
|
273
|
-
verbose_name = 'Twilio Response'
|
274
|
-
verbose_name_plural = 'Twilio Responses'
|
275
|
-
ordering = ['-created_at']
|
276
|
-
indexes = [
|
277
|
-
models.Index(fields=['message_sid']),
|
278
|
-
models.Index(fields=['verification_sid']),
|
279
|
-
models.Index(fields=['status', 'created_at']),
|
280
|
-
models.Index(fields=['response_type', 'service_type']),
|
281
|
-
]
|
282
|
-
|
283
|
-
def __str__(self):
|
284
|
-
identifier = self.message_sid or self.verification_sid or 'Unknown'
|
285
|
-
return f"{self.get_service_type_display()} {self.get_response_type_display()} - {identifier}"
|
286
|
-
|
287
|
-
@property
|
288
|
-
def is_successful(self):
|
289
|
-
"""Check if the response indicates success."""
|
290
|
-
success_statuses = ['sent', 'delivered', 'pending', 'approved']
|
291
|
-
return self.status.lower() in success_statuses if self.status else False
|
292
|
-
|
293
|
-
@property
|
294
|
-
def has_error(self):
|
295
|
-
"""Check if the response has an error."""
|
296
|
-
return bool(self.error_code or self.error_message)
|
297
|
-
|
298
|
-
|
299
206
|
class UserActivity(models.Model):
|
300
207
|
"""
|
301
208
|
User activity log.
|
@@ -26,7 +26,7 @@ class OTPRequestSerializer(serializers.Serializer):
|
|
26
26
|
source_url = serializers.URLField(
|
27
27
|
required=False,
|
28
28
|
allow_blank=True,
|
29
|
-
help_text="Source URL for tracking registration (e.g., https://
|
29
|
+
help_text="Source URL for tracking registration (e.g., https://unrealos.com)",
|
30
30
|
)
|
31
31
|
|
32
32
|
def validate_identifier(self, value):
|
@@ -76,7 +76,7 @@ class OTPVerifySerializer(serializers.Serializer):
|
|
76
76
|
source_url = serializers.URLField(
|
77
77
|
required=False,
|
78
78
|
allow_blank=True,
|
79
|
-
help_text="Source URL for tracking login (e.g., https://
|
79
|
+
help_text="Source URL for tracking login (e.g., https://unrealos.com)",
|
80
80
|
)
|
81
81
|
|
82
82
|
def validate_identifier(self, value):
|
{django_cfg-1.1.52 → django_cfg-1.1.54}/src/django_cfg/apps/accounts/services/otp_service.py
RENAMED
@@ -6,7 +6,7 @@ from typing import Optional, Tuple
|
|
6
6
|
import re
|
7
7
|
|
8
8
|
from django_cfg.modules.django_telegram import DjangoTelegram
|
9
|
-
from django_cfg.modules.django_twilio import SimpleTwilioService
|
9
|
+
from django_cfg.modules.django_twilio import SimpleTwilioService
|
10
10
|
from ..models import OTPSecret, CustomUser
|
11
11
|
from ..utils.notifications import AccountNotifications
|
12
12
|
from ..signals import notify_failed_otp_attempt
|
@@ -28,53 +28,6 @@ class OTPService:
|
|
28
28
|
phone_pattern = r'^\+[1-9]\d{6,14}$' # E.164 format: minimum 7 digits, maximum 15
|
29
29
|
return bool(re.match(phone_pattern, clean_phone))
|
30
30
|
|
31
|
-
@staticmethod
|
32
|
-
def _get_temp_email_domain(source_url: Optional[str] = None) -> str:
|
33
|
-
"""
|
34
|
-
Get domain for temporary email addresses.
|
35
|
-
|
36
|
-
Priority:
|
37
|
-
1. Extract domain from source_url
|
38
|
-
2. Use config.site_url domain
|
39
|
-
3. Fallback to 'temp.local'
|
40
|
-
"""
|
41
|
-
try:
|
42
|
-
from django_cfg.core.config import get_current_config
|
43
|
-
from urllib.parse import urlparse
|
44
|
-
|
45
|
-
config = get_current_config()
|
46
|
-
|
47
|
-
# Try to extract domain from source_url
|
48
|
-
if source_url:
|
49
|
-
try:
|
50
|
-
parsed = urlparse(source_url)
|
51
|
-
if parsed.netloc:
|
52
|
-
# Remove www. prefix if present
|
53
|
-
domain = parsed.netloc
|
54
|
-
if domain.startswith("www."):
|
55
|
-
domain = domain[4:]
|
56
|
-
return domain
|
57
|
-
except:
|
58
|
-
pass
|
59
|
-
|
60
|
-
# Try to use config.site_url domain
|
61
|
-
if config and config.site_url:
|
62
|
-
try:
|
63
|
-
parsed = urlparse(config.site_url)
|
64
|
-
if parsed.netloc:
|
65
|
-
domain = parsed.netloc
|
66
|
-
if domain.startswith("www."):
|
67
|
-
domain = domain[4:]
|
68
|
-
return domain
|
69
|
-
except:
|
70
|
-
pass
|
71
|
-
|
72
|
-
# Fallback to temp.local
|
73
|
-
return "temp.local"
|
74
|
-
|
75
|
-
except Exception:
|
76
|
-
return "temp.local"
|
77
|
-
|
78
31
|
@staticmethod
|
79
32
|
def _determine_channel(identifier: str) -> str:
|
80
33
|
"""Determine if identifier is email or phone."""
|
@@ -161,10 +114,8 @@ class OTPService:
|
|
161
114
|
user = CustomUser.objects.get(phone=cleaned_identifier)
|
162
115
|
created = False
|
163
116
|
except CustomUser.DoesNotExist:
|
164
|
-
# Create user with temp email based on phone
|
165
|
-
|
166
|
-
domain = OTPService._get_temp_email_domain(source_url)
|
167
|
-
temp_email = f"phone_{phone_clean}@{domain}"
|
117
|
+
# Create user with temp email based on phone
|
118
|
+
temp_email = f"phone_{cleaned_identifier.replace('+', '').replace(' ', '')}@temp.local"
|
168
119
|
user, created = CustomUser.objects.register_user(
|
169
120
|
temp_email, source_url=source_url
|
170
121
|
)
|
@@ -219,36 +170,10 @@ class OTPService:
|
|
219
170
|
user, otp_code, is_new_user=created, source_url=source_url, channel='email'
|
220
171
|
)
|
221
172
|
else: # phone channel
|
222
|
-
#
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
logger.error(f"❌ Failed to send phone OTP to {cleaned_identifier[:8]}: {message}")
|
227
|
-
return False, "phone_send_failed"
|
228
|
-
|
229
|
-
logger.info(f"🎉 Phone OTP sent successfully to {cleaned_identifier[:8]}: {message}")
|
230
|
-
|
231
|
-
# Send telegram notification for admin
|
232
|
-
try:
|
233
|
-
# Extract method from message (e.g., "OTP sent via WhatsApp... (Direct WhatsApp)")
|
234
|
-
method_used = "Hybrid WhatsApp"
|
235
|
-
if "(Direct WhatsApp)" in message:
|
236
|
-
method_used = "Direct WhatsApp"
|
237
|
-
elif "(Verify API)" in message:
|
238
|
-
method_used = "Verify API"
|
239
|
-
|
240
|
-
DjangoTelegram.send_info(
|
241
|
-
f"📱 Phone OTP Request ({method_used})",
|
242
|
-
{
|
243
|
-
"phone": cleaned_identifier,
|
244
|
-
"method": method_used,
|
245
|
-
"user_type": "New User" if created else "Existing User",
|
246
|
-
"source_url": source_url or "Direct",
|
247
|
-
"timestamp": timezone.now().strftime("%Y-%m-%d %H:%M:%S UTC")
|
248
|
-
}
|
249
|
-
)
|
250
|
-
except Exception as e:
|
251
|
-
logger.error(f"Failed to send telegram notification: {e}")
|
173
|
+
# Send OTP via SMS using Twilio
|
174
|
+
AccountNotifications.send_phone_otp_notification(
|
175
|
+
user, otp_code, cleaned_identifier, is_new_user=created, source_url=source_url
|
176
|
+
)
|
252
177
|
|
253
178
|
return True, "success"
|
254
179
|
except Exception as e:
|
@@ -306,47 +231,26 @@ class OTPService:
|
|
306
231
|
return None
|
307
232
|
|
308
233
|
try:
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
# Send notification for failed OTP attempt
|
317
|
-
AccountNotifications.send_failed_otp_attempt(
|
318
|
-
cleaned_identifier, channel=channel, reason=f"Verify API: {message}"
|
319
|
-
)
|
320
|
-
|
321
|
-
return None
|
322
|
-
|
323
|
-
logger.info(f"Twilio Verify API: OTP verified for {cleaned_identifier}")
|
324
|
-
|
325
|
-
except Exception as e:
|
326
|
-
logger.error(f"Twilio Verify API error during verification: {e}")
|
327
|
-
return None
|
328
|
-
else:
|
329
|
-
# Use OTPSecret for email channel
|
330
|
-
otp_secret = OTPSecret.objects.filter(
|
331
|
-
recipient=cleaned_identifier,
|
332
|
-
channel_type=channel,
|
333
|
-
secret=cleaned_otp,
|
334
|
-
is_used=False,
|
335
|
-
expires_at__gt=timezone.now(),
|
336
|
-
).first()
|
234
|
+
otp_secret = OTPSecret.objects.filter(
|
235
|
+
recipient=cleaned_identifier,
|
236
|
+
channel_type=channel,
|
237
|
+
secret=cleaned_otp,
|
238
|
+
is_used=False,
|
239
|
+
expires_at__gt=timezone.now(),
|
240
|
+
).first()
|
337
241
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
242
|
+
if not otp_secret or not otp_secret.is_valid:
|
243
|
+
logger.warning(f"Invalid OTP for {cleaned_identifier} ({channel})")
|
244
|
+
|
245
|
+
# Send notification for failed OTP attempt
|
246
|
+
AccountNotifications.send_failed_otp_attempt(
|
247
|
+
cleaned_identifier, channel=channel, reason="Invalid or expired OTP"
|
248
|
+
)
|
249
|
+
|
250
|
+
return None
|
347
251
|
|
348
|
-
|
349
|
-
|
252
|
+
# Mark OTP as used
|
253
|
+
otp_secret.mark_used()
|
350
254
|
|
351
255
|
# Get user based on channel
|
352
256
|
try:
|