django-pfx 1.7.6.dev7__tar.gz → 1.7.6.dev9__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_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/.gitlab-ci.yml +2 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/PKG-INFO +1 -1
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/django_pfx.egg-info/PKG-INFO +1 -1
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/django_pfx.egg-info/SOURCES.txt +17 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/django_pfx.egg-info/scm_file_list.json +17 -0
- django_pfx-1.7.6.dev9/django_pfx.egg-info/scm_version.json +8 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/index.rst +1 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/settings.md +37 -0
- django_pfx-1.7.6.dev9/doc/source/user_jobs.md +401 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/settings/dev.py +4 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/locale/fr/LC_MESSAGES/django.po +33 -33
- django_pfx-1.7.6.dev9/pfx/userjobs/migrations/0005_swappable_user_job.py +71 -0
- django_pfx-1.7.6.dev9/pfx/userjobs/models/__init__.py +2 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/models/user_job.py +51 -11
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/models/user_job_attachment.py +3 -1
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/providers/user_job_websocket_channel_provider.py +20 -1
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/tasks.py +8 -5
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/views/user_job_attachment_view.py +5 -2
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/views/user_job_view.py +6 -6
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/views/user_websocket_consumer.py +4 -1
- django_pfx-1.7.6.dev9/tests_custom_user_job/migrations/0001_initial.py +102 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/models.py +67 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/providers.py +19 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/scope.py +21 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/settings/common.py +133 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/settings/dev_default.py +12 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/tests/__init__.py +1 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/tests/test_user_job.py +171 -0
- django_pfx-1.7.6.dev9/tests_custom_user_job/urls.py +8 -0
- django_pfx-1.7.6.dev9/tests_user_email_login/__init__.py +0 -0
- django_pfx-1.7.6.dev9/tests_user_email_login/migrations/__init__.py +0 -0
- django_pfx-1.7.6.dev9/tests_user_email_login/settings/__init__.py +0 -0
- django_pfx-1.7.6.dev9/tests_user_email_login/settings/ci.py +18 -0
- django_pfx-1.7.6.dev9/tests_user_email_login/settings/dev.py +13 -0
- django_pfx-1.7.6.dev9/tests_user_email_login/settings/dev_custom_example.py +28 -0
- django_pfx-1.7.6.dev7/django_pfx.egg-info/scm_version.json +0 -8
- django_pfx-1.7.6.dev7/pfx/userjobs/models/__init__.py +0 -2
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/.gitignore +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/.pre-commit-config.yaml +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/LICENSE +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/MANIFEST.in +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/README.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/django_pfx.egg-info/requires.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/django_pfx.egg-info/top_level.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/Makefile +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/conf.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/api.views.rst +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/authentication.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/decorator.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/generate_openapi.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/getting_started.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/internationalisation.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/model.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/pfx_views.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/profiling.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/doc/source/testing.md +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/img/pfx.png +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/img/pfx.svg +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/make_messages +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/manage.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/apps.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/email_backend/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/email_backend/q_mailjet_email_backend.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/exceptions.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/mailjet/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/mailjet/mailjet.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/mailjet/message_result.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/migrations/0002_alter_email_headers.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/migrations/0003_alter_email_options.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/migrations/0004_alter_email_mail_from.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/models/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/models/email.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/providers/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/providers/email_django_q_executor.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/shortcuts.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/tasks.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/templates/emails/base_template.html +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/templates/emails/base_template.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/templates/emails/base_template_subject.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/views/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/emails/views/email_rest_view.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/apps.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/decorator/rest.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/default_settings.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/exceptions.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/fields/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/fields/decimal_field.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/fields/media_field.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/fields/rich_text_field.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/http/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/http/json_response.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/management/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/mfa/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/mfa/authenticator.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/mfa/base.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/mfa/email.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/mfa/legacy.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/mfa/sms.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/middleware/locale.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/middleware/request_too_big_middleware.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/middleware/ws_cookie_authentication_middleware.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/0004_alter_loginban_failed_counter_and_more.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/0005_mfadevice_mfadevice_mfa_device_unique_backend.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/0006_alter_pfxuser_managers.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/0007_alter_pfxuser_options.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/attachment_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/login_ban.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/mfa_device.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/mfa_user_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/pfx_user.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/serializers/json.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/settings.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/shortcuts.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/sms/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/sms/backends/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/sms/backends/base.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/sms/backends/console.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/storage/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/storage/exceptions.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/storage/local_storage.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/test.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/version_check.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/authentication_views.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/fields.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/filters_views.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/locale_views.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/mfa_authentication_views.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/pfxcore/views/rest_views.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/apps.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/migrations/0003_clean_scheduled_tasks.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/migrations/0004_userjob_summary_url.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/providers/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/providers/user_job_django_q_executor.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pfx/userjobs/views/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/pyproject.toml +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/requirements.txt +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/serve-doc +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/setup.cfg +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/setup.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/apps.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/channel_group_send_calls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/mfa_urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/models.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/settings/common.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/basic_api_errors.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/basic_api_test.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/mfa_test_utils.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_api_doc.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_api_doc_search.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_body_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_cache.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_client.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_emails_email_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_emails_email_model.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_emails_q_mailjet_email_backend.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_emails_send_email_message.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_fields_choices.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_fields_date.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_fields_decimal.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_fields_minutes_duration.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_fields_one2many.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_fields_rich_text.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_fields_time.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_filters.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_locale_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_mfa_legacy.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_mfa_login_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_mfa_setup_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_perm_tests.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_permissions.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_perms_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_post_migrate_groups_update.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_profiling_middleware.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_settings.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_shortcuts.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_timezone_middleware.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_tools.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_user_job_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_user_job_messages.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_user_job_model.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_user_queryset.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_user_websocket.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_version_check.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_view_decorators.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/tests/test_view_fields.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests/views.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/settings/common.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/tests/test_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_base_user/views.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/models.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/settings/common.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/tests/test_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_custom_user/views.py +0 -0
- {django_pfx-1.7.6.dev7/tests_user_email_login → django_pfx-1.7.6.dev9/tests_custom_user_job}/__init__.py +0 -0
- {django_pfx-1.7.6.dev7/tests_user_email_login → django_pfx-1.7.6.dev9/tests_custom_user_job}/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev7/tests_user_email_login → django_pfx-1.7.6.dev9/tests_custom_user_job}/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev7/tests_user_email_login → django_pfx-1.7.6.dev9/tests_custom_user_job}/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev7/tests_user_email_login → django_pfx-1.7.6.dev9/tests_custom_user_job}/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev7/tests_user_email_login → django_pfx-1.7.6.dev9/tests_custom_user_job}/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/models.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/settings/common.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/tests/test_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/urls.py +0 -0
- {django_pfx-1.7.6.dev7 → django_pfx-1.7.6.dev9}/tests_user_email_login/views.py +0 -0
|
@@ -39,6 +39,7 @@ quality check:
|
|
|
39
39
|
- python manage.py makemigrations tests_base_user --check --dry-run
|
|
40
40
|
- python manage.py makemigrations tests_custom_user --check --dry-run
|
|
41
41
|
- python manage.py makemigrations tests_user_email_login --check --dry-run
|
|
42
|
+
- python manage.py makemigrations tests_custom_user_job --check --dry-run
|
|
42
43
|
|
|
43
44
|
test:
|
|
44
45
|
image: $IMAGE
|
|
@@ -54,6 +55,7 @@ test:
|
|
|
54
55
|
- coverage run -a --source='.' manage.py test tests_base_user
|
|
55
56
|
- coverage run -a --source='.' manage.py test tests_custom_user
|
|
56
57
|
- coverage run -a --source='.' manage.py test tests_user_email_login
|
|
58
|
+
- coverage run -a --source='.' manage.py test tests_custom_user_job
|
|
57
59
|
- coverage report
|
|
58
60
|
- coverage xml
|
|
59
61
|
- coverage html
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.7.6.
|
|
3
|
+
Version: 1.7.6.dev9
|
|
4
4
|
Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.7.6.
|
|
3
|
+
Version: 1.7.6.dev9
|
|
4
4
|
Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
@@ -32,6 +32,7 @@ doc/source/pfx_views.md
|
|
|
32
32
|
doc/source/profiling.md
|
|
33
33
|
doc/source/settings.md
|
|
34
34
|
doc/source/testing.md
|
|
35
|
+
doc/source/user_jobs.md
|
|
35
36
|
img/pfx.png
|
|
36
37
|
img/pfx.svg
|
|
37
38
|
pfx/__init__.py
|
|
@@ -177,6 +178,7 @@ pfx/userjobs/migrations/0001_initial.py
|
|
|
177
178
|
pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py
|
|
178
179
|
pfx/userjobs/migrations/0003_clean_scheduled_tasks.py
|
|
179
180
|
pfx/userjobs/migrations/0004_userjob_summary_url.py
|
|
181
|
+
pfx/userjobs/migrations/0005_swappable_user_job.py
|
|
180
182
|
pfx/userjobs/migrations/__init__.py
|
|
181
183
|
pfx/userjobs/models/__init__.py
|
|
182
184
|
pfx/userjobs/models/user_job.py
|
|
@@ -276,6 +278,21 @@ tests_custom_user/settings/dev_default.py
|
|
|
276
278
|
tests_custom_user/tests/__init__.py
|
|
277
279
|
tests_custom_user/tests/test_api.py
|
|
278
280
|
tests_custom_user/tests/test_auth_api.py
|
|
281
|
+
tests_custom_user_job/__init__.py
|
|
282
|
+
tests_custom_user_job/models.py
|
|
283
|
+
tests_custom_user_job/providers.py
|
|
284
|
+
tests_custom_user_job/scope.py
|
|
285
|
+
tests_custom_user_job/urls.py
|
|
286
|
+
tests_custom_user_job/migrations/0001_initial.py
|
|
287
|
+
tests_custom_user_job/migrations/__init__.py
|
|
288
|
+
tests_custom_user_job/settings/__init__.py
|
|
289
|
+
tests_custom_user_job/settings/ci.py
|
|
290
|
+
tests_custom_user_job/settings/common.py
|
|
291
|
+
tests_custom_user_job/settings/dev.py
|
|
292
|
+
tests_custom_user_job/settings/dev_custom_example.py
|
|
293
|
+
tests_custom_user_job/settings/dev_default.py
|
|
294
|
+
tests_custom_user_job/tests/__init__.py
|
|
295
|
+
tests_custom_user_job/tests/test_user_job.py
|
|
279
296
|
tests_user_email_login/__init__.py
|
|
280
297
|
tests_user_email_login/models.py
|
|
281
298
|
tests_user_email_login/urls.py
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"doc/source/profiling.md",
|
|
21
21
|
"doc/source/settings.md",
|
|
22
22
|
"doc/source/testing.md",
|
|
23
|
+
"doc/source/user_jobs.md",
|
|
23
24
|
"img/pfx.png",
|
|
24
25
|
"img/pfx.svg",
|
|
25
26
|
"make_messages",
|
|
@@ -163,6 +164,7 @@
|
|
|
163
164
|
"pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py",
|
|
164
165
|
"pfx/userjobs/migrations/0003_clean_scheduled_tasks.py",
|
|
165
166
|
"pfx/userjobs/migrations/0004_userjob_summary_url.py",
|
|
167
|
+
"pfx/userjobs/migrations/0005_swappable_user_job.py",
|
|
166
168
|
"pfx/userjobs/migrations/__init__.py",
|
|
167
169
|
"pfx/userjobs/models/__init__.py",
|
|
168
170
|
"pfx/userjobs/models/user_job.py",
|
|
@@ -269,6 +271,21 @@
|
|
|
269
271
|
"tests_custom_user/tests/test_auth_api.py",
|
|
270
272
|
"tests_custom_user/urls.py",
|
|
271
273
|
"tests_custom_user/views.py",
|
|
274
|
+
"tests_custom_user_job/__init__.py",
|
|
275
|
+
"tests_custom_user_job/migrations/0001_initial.py",
|
|
276
|
+
"tests_custom_user_job/migrations/__init__.py",
|
|
277
|
+
"tests_custom_user_job/models.py",
|
|
278
|
+
"tests_custom_user_job/providers.py",
|
|
279
|
+
"tests_custom_user_job/scope.py",
|
|
280
|
+
"tests_custom_user_job/settings/__init__.py",
|
|
281
|
+
"tests_custom_user_job/settings/ci.py",
|
|
282
|
+
"tests_custom_user_job/settings/common.py",
|
|
283
|
+
"tests_custom_user_job/settings/dev.py",
|
|
284
|
+
"tests_custom_user_job/settings/dev_custom_example.py",
|
|
285
|
+
"tests_custom_user_job/settings/dev_default.py",
|
|
286
|
+
"tests_custom_user_job/tests/__init__.py",
|
|
287
|
+
"tests_custom_user_job/tests/test_user_job.py",
|
|
288
|
+
"tests_custom_user_job/urls.py",
|
|
272
289
|
"tests_user_email_login/__init__.py",
|
|
273
290
|
"tests_user_email_login/migrations/0001_initial.py",
|
|
274
291
|
"tests_user_email_login/migrations/__init__.py",
|
|
@@ -93,3 +93,40 @@ Refer to [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/H
|
|
|
93
93
|
|
|
94
94
|
### PFX_FORCE_USER_FILTERED_QUERYSET
|
|
95
95
|
Force the use of a specific queryset that is segregated by user.
|
|
96
|
+
|
|
97
|
+
## User jobs
|
|
98
|
+
|
|
99
|
+
See [User jobs](./user_jobs.md).
|
|
100
|
+
|
|
101
|
+
### PFX_USER_JOB_MODEL
|
|
102
|
+
The model used for user jobs, as an `app_label.ModelName` reference
|
|
103
|
+
(optional, default `userjobs.UserJob`).
|
|
104
|
+
|
|
105
|
+
It must inherit `pfx.userjobs.models.AbstractUserJob`, see
|
|
106
|
+
[Extending the model](./user_jobs.md#extending-the-model).
|
|
107
|
+
|
|
108
|
+
### PFX_USER_JOB_EXECUTOR
|
|
109
|
+
The class queuing the jobs and registering the periodic tasks
|
|
110
|
+
(optional, default `pfx.userjobs.providers.UserJobDjangoQExecutor`).
|
|
111
|
+
|
|
112
|
+
### PFX_USER_JOB_WEBSOCKET_PROVIDER
|
|
113
|
+
The class sending the job status changes to the clients
|
|
114
|
+
(optional, default
|
|
115
|
+
`pfx.userjobs.providers.UserJobWebsocketChannelProvider`).
|
|
116
|
+
|
|
117
|
+
Override it to narrow the audience of a job, see
|
|
118
|
+
[Providers](./user_jobs.md#providers).
|
|
119
|
+
|
|
120
|
+
### PFX_USER_JOB_TIMEOUT_SUMMARY
|
|
121
|
+
The summary given to a job failed for exceeding its timeout
|
|
122
|
+
(optional, default *Timeout exceeds. Our support team has been
|
|
123
|
+
notified.*).
|
|
124
|
+
|
|
125
|
+
### PFX_USER_JOB_NEVER_STARTED_SUMMARY
|
|
126
|
+
The summary given to a job failed for never having started
|
|
127
|
+
(optional, default *This job never started. Our support team has been
|
|
128
|
+
notified.*).
|
|
129
|
+
|
|
130
|
+
### USER_JOB_FAILED_SHELF_LIFE_HOURS
|
|
131
|
+
How long a failed job is kept, in hours, unless its own `shelf_life` is
|
|
132
|
+
longer (optional, default `24`).
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# User jobs
|
|
2
|
+
|
|
3
|
+
A user job is a long task run out of the request/response cycle, on
|
|
4
|
+
behalf of the user who started it: an export, a mailing, a document
|
|
5
|
+
generation. The API answers immediately with the job, the task runs in a
|
|
6
|
+
worker, and its progress is pushed back to the browser over a websocket.
|
|
7
|
+
|
|
8
|
+
PFX keeps the job itself: its status, its result summary, the files it
|
|
9
|
+
produced, and how long all of that is kept.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
The app needs a queue and a channel layer:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install django-q2
|
|
17
|
+
pip install channels[daphne] channels-redis
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
# settings.py
|
|
22
|
+
INSTALLED_APPS = [
|
|
23
|
+
...
|
|
24
|
+
'pfx.userjobs',
|
|
25
|
+
'django_q',
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Both are checked when the app is loaded, and are replaceable, see
|
|
30
|
+
[Providers](#providers).
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
# urls.py
|
|
34
|
+
urlpatterns = [
|
|
35
|
+
path('api/', include('pfx.userjobs.urls')),
|
|
36
|
+
]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
# asgi.py
|
|
41
|
+
from pfx.userjobs.urls import websocket_urlpatterns as userjobs_ws
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Writing a job
|
|
45
|
+
|
|
46
|
+
A job is a module level function taking the job as first argument,
|
|
47
|
+
followed by the arguments it was created with:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
def export_books(job, author_pk):
|
|
51
|
+
books = Book.objects.filter(author_id=author_pk)
|
|
52
|
+
return dict(summary=f"{books.count()} books exported.")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
It is found by its dotted path, so it must be importable and stay where
|
|
56
|
+
it is as long as jobs referencing it may still be queued.
|
|
57
|
+
|
|
58
|
+
### The result
|
|
59
|
+
|
|
60
|
+
The returned dictionary describes what the user is shown:
|
|
61
|
+
|
|
62
|
+
| Key | |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `status` | `done` (the default), `warning` or `failed` |
|
|
65
|
+
| `summary` | one line, shown as the result of the job |
|
|
66
|
+
| `summary_url` | where the summary links to |
|
|
67
|
+
| `message` | a longer text, shown when the job is opened |
|
|
68
|
+
| `stacktrace` | technical details, for a failure |
|
|
69
|
+
|
|
70
|
+
An exception that escapes the function fails the job: the summary
|
|
71
|
+
becomes `settings.PFX_SYSTEM_ERROR_USER_MESSAGE` and the traceback is
|
|
72
|
+
kept in `stacktrace`. Nothing is shown to the user that the function did
|
|
73
|
+
not choose to show.
|
|
74
|
+
|
|
75
|
+
`failed()` reports a failure the function handles itself:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
def export_books(job, author_pk):
|
|
79
|
+
try:
|
|
80
|
+
...
|
|
81
|
+
except SourceUnavailable:
|
|
82
|
+
job.failed(summary="The catalog is unavailable, try again later.")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Attachments
|
|
86
|
+
|
|
87
|
+
A job produces files through `UserJobAttachment`. They are listed in the
|
|
88
|
+
job and downloaded from the job REST API:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from pfx.userjobs.models import UserJobAttachment
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def export_books(job, author_pk):
|
|
95
|
+
attachment = UserJobAttachment.objects.create(job=job, is_result=True)
|
|
96
|
+
attachment.file = UserJobAttachment._meta.get_field('file').upload(
|
|
97
|
+
attachment, pdf_bytes, "books.pdf")
|
|
98
|
+
attachment.save()
|
|
99
|
+
return dict(summary="The export is ready.")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
They are deleted with the job, files included.
|
|
103
|
+
|
|
104
|
+
### Jobs that outlive the worker
|
|
105
|
+
|
|
106
|
+
A function returning `status='started'` leaves the job running: PFX does
|
|
107
|
+
not terminate it. Use it for a task handed over to a third party, and
|
|
108
|
+
call `terminated()` or `failed()` from wherever the answer comes back:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
def send_campaign(job, campaign_pk):
|
|
112
|
+
provider.send(campaign_pk, callback_job=job.pk)
|
|
113
|
+
return dict(status=job.STATUS_STARTED)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Such a job is still failed after its `timeout`, see
|
|
117
|
+
[Life cycle](#life-cycle).
|
|
118
|
+
|
|
119
|
+
## Starting a job
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from pfx.userjobs.models import get_user_job_model
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class BookRestView(RestView):
|
|
126
|
+
@rest_api("/<int:pk>/export", method="get")
|
|
127
|
+
def export(self, pk, *args, **kwargs):
|
|
128
|
+
job = get_user_job_model().objects.create(
|
|
129
|
+
name="Books export",
|
|
130
|
+
user=self.request.user,
|
|
131
|
+
func='myapp.jobs.export_books',
|
|
132
|
+
args=[pk],
|
|
133
|
+
timeout=300)
|
|
134
|
+
return JsonResponse(dict(job=job.pk))
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
| Field | |
|
|
138
|
+
| --- | --- |
|
|
139
|
+
| `name` | what the user sees in their job list |
|
|
140
|
+
| `user` | who started it, and who may see it |
|
|
141
|
+
| `func` | dotted path of the function |
|
|
142
|
+
| `args` | its arguments, stored as JSON |
|
|
143
|
+
| `timeout` | in seconds, given to the queue |
|
|
144
|
+
| `cluster` | the queue cluster, `default` |
|
|
145
|
+
| `shelf_life` | how long the finished job is kept, 30 days |
|
|
146
|
+
| `lock` | see [One job at a time](#one-job-at-a-time) |
|
|
147
|
+
| `auto_queued` | queue the job on save, `True` |
|
|
148
|
+
|
|
149
|
+
Creating the job queues it. With `auto_queued=False` it is created idle
|
|
150
|
+
and queued later by `enqueue()`, which is what a job created outside of
|
|
151
|
+
a request usually wants.
|
|
152
|
+
|
|
153
|
+
Always go through `get_user_job_model()` rather than importing `UserJob`:
|
|
154
|
+
the model is swappable, see [Extending the model](#extending-the-model).
|
|
155
|
+
|
|
156
|
+
### One job at a time
|
|
157
|
+
|
|
158
|
+
A non empty `lock` is unique among the jobs that are not finished: as
|
|
159
|
+
long as one is queued or running, creating another with the same lock
|
|
160
|
+
raises an `APIError`, answered as *This operation is already in
|
|
161
|
+
progress.*
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
get_user_job_model().objects.create(
|
|
165
|
+
...,
|
|
166
|
+
lock=f'export-books-{author.pk}')
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Life cycle
|
|
170
|
+
|
|
171
|
+
```{mermaid}
|
|
172
|
+
|
|
173
|
+
stateDiagram-v2
|
|
174
|
+
[*] --> not_queued: created
|
|
175
|
+
not_queued --> queued: enqueue()
|
|
176
|
+
queued --> started: the worker picks it up
|
|
177
|
+
started --> done: the function returns
|
|
178
|
+
started --> warning: status='warning'
|
|
179
|
+
started --> failed: status='failed', or an exception
|
|
180
|
+
done --> [*]: shelf life
|
|
181
|
+
warning --> [*]: shelf life
|
|
182
|
+
failed --> [*]: shelf life
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Two periodic tasks watch over the jobs, registered on the queue after
|
|
187
|
+
each `migrate`:
|
|
188
|
+
|
|
189
|
+
`pfx.userjobs.tasks.task_user_jobs_update`, every 30 minutes, closes the
|
|
190
|
+
jobs nothing else will:
|
|
191
|
+
|
|
192
|
+
- a job whose queue task failed,
|
|
193
|
+
- a job still `started` more than `timeout` + 5 minutes after it started,
|
|
194
|
+
failed with `PFX_USER_JOB_TIMEOUT_SUMMARY`,
|
|
195
|
+
- a job still `queued` after 3 days, failed with
|
|
196
|
+
`PFX_USER_JOB_NEVER_STARTED_SUMMARY`,
|
|
197
|
+
- a job still `not_queued` after 3 days, deleted.
|
|
198
|
+
|
|
199
|
+
`pfx.userjobs.tasks.task_user_jobs_cleanup`, every night, deletes the
|
|
200
|
+
finished jobs past their retention, see [Retention](#retention).
|
|
201
|
+
|
|
202
|
+
## Retention
|
|
203
|
+
|
|
204
|
+
A job is kept for its `shelf_life` after it ended, 30 days by default. A
|
|
205
|
+
negative `shelf_life` keeps it forever.
|
|
206
|
+
|
|
207
|
+
Failed jobs are kept for `USER_JOB_FAILED_SHELF_LIFE_HOURS` hours, 24 by
|
|
208
|
+
default, unless their own `shelf_life` is longer: a failure is of no use
|
|
209
|
+
to the user once they have read it, but a job worth keeping stays.
|
|
210
|
+
|
|
211
|
+
Deleting a job deletes its attachments and their files.
|
|
212
|
+
|
|
213
|
+
## The REST API
|
|
214
|
+
|
|
215
|
+
`/user-jobs/user-jobs` lists the jobs of the authenticated user, most
|
|
216
|
+
recent first. A user only ever sees their own jobs.
|
|
217
|
+
|
|
218
|
+
| | |
|
|
219
|
+
| --- | --- |
|
|
220
|
+
| `GET /user-jobs/user-jobs` | the list |
|
|
221
|
+
| `GET /user-jobs/user-jobs/<pk>` | one job and its attachments |
|
|
222
|
+
| `PUT /user-jobs/user-jobs/<pk>` | mark as read |
|
|
223
|
+
| `DELETE /user-jobs/user-jobs/<pk>` | delete a finished job |
|
|
224
|
+
| `GET /user-jobs/attachments/<pk>/file` | download an attachment |
|
|
225
|
+
|
|
226
|
+
Everything is read only but `read_status`: writing the current status of
|
|
227
|
+
the job into it marks the job as read. The `unread` filter returns the
|
|
228
|
+
jobs whose `read_status` does not match their `status` yet, which is what
|
|
229
|
+
a notification badge counts.
|
|
230
|
+
|
|
231
|
+
The list also filters on `status`, on `status_exclude` and on
|
|
232
|
+
`created_after`.
|
|
233
|
+
|
|
234
|
+
A job can only be deleted once it is `done`, `warning` or `failed`, a
|
|
235
|
+
superuser excepted.
|
|
236
|
+
|
|
237
|
+
## The websocket
|
|
238
|
+
|
|
239
|
+
The client connects to `ws/user` and receives a message on every status
|
|
240
|
+
change of its jobs:
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"model": "base.UserJob",
|
|
245
|
+
"event": "status_changed",
|
|
246
|
+
"item": {"pk": 12, "status": {"value": "done", "label": "Completed"}}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`event` is `status_changed` or `deleted`, and `item` carries the job as
|
|
251
|
+
the REST API returns it, with its attachments.
|
|
252
|
+
|
|
253
|
+
`model` discriminates the message among the producers sharing the
|
|
254
|
+
channel. Despite what it looks like it is a wire protocol value, not a
|
|
255
|
+
model reference: it stays `base.UserJob` whatever
|
|
256
|
+
`PFX_USER_JOB_MODEL` is, because `react-pfx` matches it as is. Making it
|
|
257
|
+
follow the model would silently stop the clients from showing
|
|
258
|
+
anything.
|
|
259
|
+
|
|
260
|
+
The message is sent to the channel group of the user, and the client
|
|
261
|
+
subscribes to the same group when it connects. Both names come from the
|
|
262
|
+
websocket provider, which is where a project narrows the audience of a
|
|
263
|
+
job, see [Providers](#providers).
|
|
264
|
+
|
|
265
|
+
## Extending the model
|
|
266
|
+
|
|
267
|
+
`UserJob` knows nothing about the domain of a project, and that is often
|
|
268
|
+
not enough: a multi-tenant project segregating its data by tenant needs
|
|
269
|
+
the jobs to be segregated too, otherwise a user working on several
|
|
270
|
+
tenants sees, on one of them, the results of the jobs they started on
|
|
271
|
+
another.
|
|
272
|
+
|
|
273
|
+
The model is swappable. `AbstractUserJob` carries every field and the
|
|
274
|
+
whole machinery, and the replacement inherits it: fields, constraints,
|
|
275
|
+
managers and behaviour can be added, nothing has to be rewritten.
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
# myapp/models.py
|
|
279
|
+
from pfx.userjobs.models import AbstractUserJob
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class UserJobQuerySet(TenantQuerySetMixin, PFXUserJobQuerySet):
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class UserJob(AbstractUserJob):
|
|
287
|
+
tenant = models.ForeignKey(Tenant, on_delete=models.PROTECT)
|
|
288
|
+
|
|
289
|
+
objects = UserJobQuerySet.as_manager()
|
|
290
|
+
|
|
291
|
+
class Meta(AbstractUserJob.Meta):
|
|
292
|
+
db_table = 'myapp_user_job'
|
|
293
|
+
|
|
294
|
+
def save(self, *args, **kw):
|
|
295
|
+
if self.tenant_id is None:
|
|
296
|
+
self.tenant = current_tenant()
|
|
297
|
+
return super().save(*args, **kw)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
# settings.py
|
|
302
|
+
PFX_USER_JOB_MODEL = 'myapp.UserJob'
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The setting takes an `app_label.ModelName`, the Django convention for a
|
|
306
|
+
swappable model, as `AUTH_USER_MODEL`.
|
|
307
|
+
|
|
308
|
+
`objects` is the manager the project narrows, and the one the rest views
|
|
309
|
+
and the attachments go through. `all_objects`, inherited from
|
|
310
|
+
`AbstractUserJob`, must stay unnarrowed: the worker running a job and
|
|
311
|
+
the periodic tasks use it, and they run outside of any request.
|
|
312
|
+
|
|
313
|
+
### The migration
|
|
314
|
+
|
|
315
|
+
`userjobs.0005_swappable_user_job` repoints the attachments to the
|
|
316
|
+
swapped model, so it has to run after the migration creating it. PFX
|
|
317
|
+
depends on the *first* migration of its app, which is not enough as soon
|
|
318
|
+
as the model is added later: the project migration declares the
|
|
319
|
+
ordering.
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
class Migration(migrations.Migration):
|
|
323
|
+
dependencies = [('userjobs', '0004_userjob_summary_url')]
|
|
324
|
+
run_before = [('userjobs', '0005_swappable_user_job')]
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Nothing else is needed: the foreign key of the attachments follows the
|
|
328
|
+
setting, PFX alters it itself.
|
|
329
|
+
|
|
330
|
+
### Taking over the PFX table
|
|
331
|
+
|
|
332
|
+
Swapping the model leaves the `userjobs_user_job` table behind, with the
|
|
333
|
+
jobs it holds and the attachments pointing at them. PFX drops it if it
|
|
334
|
+
is empty; a project with jobs to keep takes it over in the migration
|
|
335
|
+
above, instead of creating a table of its own. Renaming is enough, the
|
|
336
|
+
columns are the ones of `AbstractUserJob`:
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
operations = [
|
|
340
|
+
migrations.SeparateDatabaseAndState(
|
|
341
|
+
state_operations=[
|
|
342
|
+
migrations.CreateModel(name='UserJob', ...)],
|
|
343
|
+
database_operations=[migrations.RunSQL(
|
|
344
|
+
"ALTER TABLE userjobs_user_job RENAME TO myapp_user_job;",
|
|
345
|
+
"ALTER TABLE myapp_user_job RENAME TO userjobs_user_job;")]),
|
|
346
|
+
migrations.AddField(model_name='userjob', name='tenant', ...),
|
|
347
|
+
]
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
A field the jobs cannot do without is added nullable, filled from what
|
|
351
|
+
is known of the existing jobs, then made mandatory.
|
|
352
|
+
|
|
353
|
+
`ALTER TABLE ... RENAME` leaves the indexes named after the old table.
|
|
354
|
+
Only the unique constraint on `lock` has to follow, Django knows it by
|
|
355
|
+
name:
|
|
356
|
+
|
|
357
|
+
```sql
|
|
358
|
+
ALTER INDEX unique_active_lock
|
|
359
|
+
RENAME TO myapp_userjob_unique_active_lock;
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### What the project still owns
|
|
363
|
+
|
|
364
|
+
The swap is invisible to PFX, which always goes through
|
|
365
|
+
`get_user_job_model()`, but not to the project: a
|
|
366
|
+
`from pfx.userjobs.models import UserJob` left anywhere in it now points
|
|
367
|
+
at the swapped out model, and raises *Manager isn't available* the
|
|
368
|
+
first time it is used.
|
|
369
|
+
|
|
370
|
+
## Providers
|
|
371
|
+
|
|
372
|
+
Two classes are replaceable, both validated when the app is loaded.
|
|
373
|
+
|
|
374
|
+
`PFX_USER_JOB_EXECUTOR` queues the jobs and registers the periodic
|
|
375
|
+
tasks. The default one uses Django Q2.
|
|
376
|
+
|
|
377
|
+
`PFX_USER_JOB_WEBSOCKET_PROVIDER` sends the status changes. Its two
|
|
378
|
+
group names are what a project overrides to narrow the audience of a
|
|
379
|
+
job, and they must stay consistent with each other:
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
class TenantUserJobWebsocketChannelProvider(UserJobWebsocketChannelProvider):
|
|
383
|
+
@classmethod
|
|
384
|
+
def group_name(cls, job):
|
|
385
|
+
"""The group notified of a status change."""
|
|
386
|
+
return f'group_user_{job.user.pk}_tenant_{job.tenant_id}'
|
|
387
|
+
|
|
388
|
+
@classmethod
|
|
389
|
+
def consumer_group_name(cls, consumer):
|
|
390
|
+
"""The group a client subscribes to."""
|
|
391
|
+
return (
|
|
392
|
+
f'group_user_{consumer.user.pk}'
|
|
393
|
+
f'_tenant_{consumer.scope["tenant"].pk}')
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The consumer is passed whole so that an override can use its scope, set
|
|
397
|
+
by the websocket middlewares, and not only its user.
|
|
398
|
+
|
|
399
|
+
## Settings
|
|
400
|
+
|
|
401
|
+
See [Settings](./settings.md#user-jobs).
|
|
@@ -8,6 +8,10 @@ INSTALLED_APPS = [
|
|
|
8
8
|
'pfx.emails',
|
|
9
9
|
]
|
|
10
10
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
11
|
+
|
|
12
|
+
# The swappable user job model must be resolvable by the migration
|
|
13
|
+
# autodetector.
|
|
14
|
+
PFX_USER_JOB_MODEL = 'userjobs.UserJob'
|
|
11
15
|
STORAGE_DEFAULT = 'pfx.pfxcore.storage.LocalStorage'
|
|
12
16
|
|
|
13
17
|
CACHE_MEMORY = {
|