wbcore 1.46.0__py2.py3-none-any.whl → 1.58.2__py2.py3-none-any.whl
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.
- wbcore/cache/decorators.py +5 -3
- wbcore/cache/registry.py +14 -7
- wbcore/configs/__init__.py +1 -0
- wbcore/configs/configs.py +5 -0
- wbcore/configs/decorators.py +1 -1
- wbcore/configurations/configurations/apps.py +3 -2
- wbcore/configurations/configurations/authentication.py +1 -1
- wbcore/configurations/configurations/base.py +1 -1
- wbcore/configurations/configurations/cache.py +1 -1
- wbcore/configurations/configurations/i18nl10n.py +2 -1
- wbcore/configurations/configurations/maintenance.py +1 -1
- wbcore/configurations/configurations/media.py +1 -1
- wbcore/configurations/configurations/middleware.py +1 -1
- wbcore/configurations/configurations/rest_framework.py +1 -1
- wbcore/configurations/configurations/static.py +3 -3
- wbcore/configurations/configurations/wbcore.py +1 -1
- wbcore/content_type/serializers.py +13 -5
- wbcore/content_type/utils.py +3 -3
- wbcore/content_type/viewsets.py +2 -2
- wbcore/contrib/agenda/filters/calendar_item.py +5 -4
- wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po +145 -52
- wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po.translated +236 -0
- wbcore/contrib/agenda/locale/en/LC_MESSAGES/django.po +200 -0
- wbcore/contrib/agenda/locale/fr/LC_MESSAGES/django.po +201 -0
- wbcore/contrib/agenda/viewsets/calendar_items.py +7 -7
- wbcore/contrib/agenda/viewsets/menu/calendar_items.py +0 -6
- wbcore/contrib/ai/exceptions.py +5 -5
- wbcore/contrib/ai/llm/config.py +76 -27
- wbcore/contrib/ai/llm/mixins.py +5 -8
- wbcore/contrib/ai/llm/utils.py +50 -26
- wbcore/contrib/authentication/admin.py +2 -2
- wbcore/contrib/authentication/factories/__init__.py +8 -1
- wbcore/contrib/authentication/factories/users.py +19 -0
- wbcore/contrib/authentication/filters.py +1 -2
- wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po +209 -187
- wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po.translated +634 -0
- wbcore/contrib/authentication/locale/en/LC_MESSAGES/django.po +590 -0
- wbcore/contrib/authentication/locale/fr/LC_MESSAGES/django.po +592 -0
- wbcore/contrib/authentication/models/users.py +3 -3
- wbcore/contrib/authentication/models/users_activities.py +1 -1
- wbcore/contrib/authentication/serializers/users.py +2 -2
- wbcore/contrib/authentication/tests/test_tokens.py +3 -3
- wbcore/contrib/authentication/tests/test_users.py +0 -1
- wbcore/contrib/authentication/urls.py +0 -4
- wbcore/contrib/authentication/viewsets/endpoints/user_activities.py +2 -11
- wbcore/contrib/authentication/viewsets/endpoints/users.py +0 -3
- wbcore/contrib/authentication/viewsets/user_activities.py +2 -1
- wbcore/contrib/authentication/viewsets/users.py +6 -4
- wbcore/contrib/color/models.py +2 -1
- wbcore/contrib/currency/factories.py +1 -1
- wbcore/contrib/currency/import_export/backends/fixerio/currency_fx_rates.py +3 -1
- wbcore/contrib/currency/models.py +30 -8
- wbcore/contrib/currency/serializers.py +5 -1
- wbcore/contrib/currency/tests/test_serializers.py +7 -3
- wbcore/contrib/currency/tests/test_viewsets.py +1 -1
- wbcore/contrib/currency/viewsets/currency.py +2 -2
- wbcore/contrib/currency/viewsets/endpoints/currency_fx_rates.py +0 -9
- wbcore/contrib/dataloader/tests/test/dataloaders/protocols.py +1 -2
- wbcore/contrib/dataloader/utils.py +2 -2
- wbcore/contrib/directory/factories/__init__.py +1 -1
- wbcore/contrib/directory/factories/entries.py +2 -1
- wbcore/contrib/directory/filters/entries.py +9 -0
- wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po +728 -714
- wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po.translated +1779 -0
- wbcore/contrib/directory/locale/en/LC_MESSAGES/django.po +1652 -0
- wbcore/contrib/directory/locale/fr/LC_MESSAGES/django.po +1654 -0
- wbcore/contrib/directory/migrations/0011_person_description_person_i18n.py +24 -0
- wbcore/contrib/directory/migrations/0012_alter_person_managers.py +20 -0
- wbcore/contrib/directory/migrations/0013_alter_clientmanagerrelationship_options.py +17 -0
- wbcore/contrib/directory/models/contacts.py +2 -2
- wbcore/contrib/directory/models/entries.py +31 -5
- wbcore/contrib/directory/models/relationships.py +31 -35
- wbcore/contrib/directory/permissions.py +6 -0
- wbcore/contrib/directory/serializers/companies.py +16 -8
- wbcore/contrib/directory/serializers/contacts.py +8 -8
- wbcore/contrib/directory/serializers/entries.py +26 -15
- wbcore/contrib/directory/serializers/entry_representations.py +4 -2
- wbcore/contrib/directory/serializers/persons.py +12 -10
- wbcore/contrib/directory/serializers/relationships.py +2 -2
- wbcore/contrib/directory/tests/conftest.py +2 -0
- wbcore/contrib/directory/tests/disable_signals.py +11 -1
- wbcore/contrib/directory/tests/signals.py +2 -2
- wbcore/contrib/directory/tests/test_models.py +88 -66
- wbcore/contrib/directory/tests/test_serializers.py +1 -1
- wbcore/contrib/directory/tests/test_viewsets.py +8 -8
- wbcore/contrib/directory/viewsets/buttons/__init__.py +1 -1
- wbcore/contrib/directory/viewsets/buttons/relationships.py +32 -0
- wbcore/contrib/directory/viewsets/contacts.py +6 -6
- wbcore/contrib/directory/viewsets/display/__init__.py +1 -1
- wbcore/contrib/directory/viewsets/display/contacts.py +1 -14
- wbcore/contrib/directory/viewsets/display/entries.py +68 -38
- wbcore/contrib/directory/viewsets/display/relationships.py +26 -50
- wbcore/contrib/directory/viewsets/endpoints/relationships.py +1 -26
- wbcore/contrib/directory/viewsets/entries.py +8 -6
- wbcore/contrib/directory/viewsets/previews/entries.py +3 -3
- wbcore/contrib/directory/viewsets/relationships.py +16 -2
- wbcore/contrib/directory/viewsets/titles/relationships.py +2 -3
- wbcore/contrib/documents/filters.py +0 -2
- wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po +103 -94
- wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po.translated +285 -0
- wbcore/contrib/documents/locale/en/LC_MESSAGES/django.po +271 -0
- wbcore/contrib/documents/locale/fr/LC_MESSAGES/django.po +270 -0
- wbcore/contrib/documents/tests/test_models.py +32 -28
- wbcore/contrib/documents/viewsets/endpoints/shareable_links.py +2 -21
- wbcore/contrib/dynamic_preferences/types.py +108 -0
- wbcore/contrib/dynamic_preferences/viewsets.py +27 -0
- wbcore/contrib/example_app/filters/event.py +3 -1
- wbcore/contrib/example_app/filters/match.py +1 -1
- wbcore/contrib/example_app/models.py +91 -22
- wbcore/contrib/example_app/serializers/person_team.py +4 -4
- wbcore/contrib/example_app/templates/example_app/embedded_view.html +19 -0
- wbcore/contrib/example_app/tests/e2e/test_teams.py +1 -1
- wbcore/contrib/example_app/tests/test_models/test_match.py +17 -7
- wbcore/contrib/example_app/urls.py +2 -0
- wbcore/contrib/example_app/views.py +7 -0
- wbcore/contrib/example_app/viewsets/displays/team.py +23 -4
- wbcore/contrib/example_app/viewsets/menu/menus.py +1 -1
- wbcore/contrib/example_app/viewsets/menus.py +1 -1
- wbcore/contrib/geography/tests/conftest.py +14 -0
- wbcore/contrib/geography/tests/test_models.py +23 -8
- wbcore/contrib/geography/tests/test_viewsets.py +96 -2
- wbcore/contrib/guardian/tests/test_model_mixins.py +3 -4
- wbcore/contrib/guardian/tests/test_tasks.py +9 -9
- wbcore/contrib/guardian/tests/test_viewsets.py +2 -2
- wbcore/contrib/guardian/viewsets/configs/__init__.py +1 -1
- wbcore/contrib/guardian/viewsets/configs/buttons.py +9 -0
- wbcore/contrib/guardian/viewsets/configs/endpoints.py +7 -0
- wbcore/contrib/guardian/viewsets/viewsets.py +2 -0
- wbcore/contrib/i18n/__init__.py +2 -0
- wbcore/contrib/i18n/buttons.py +33 -0
- wbcore/contrib/i18n/serializers/__init__.py +0 -0
- wbcore/contrib/i18n/serializers/fields.py +20 -0
- wbcore/contrib/i18n/serializers/mixins.py +13 -0
- wbcore/contrib/i18n/tests/conftest.py +11 -0
- wbcore/contrib/i18n/tests/test_viewsets.py +67 -0
- wbcore/contrib/i18n/translation.py +140 -0
- wbcore/contrib/i18n/viewsets.py +36 -0
- wbcore/contrib/icons/backends/default.py +1 -0
- wbcore/contrib/icons/backends/material.py +1 -0
- wbcore/contrib/icons/icons.py +5 -8
- wbcore/contrib/io/admin.py +1 -0
- wbcore/contrib/io/backends/mail.py +3 -2
- wbcore/contrib/io/backends/utils.py +14 -17
- wbcore/contrib/io/exceptions.py +8 -0
- wbcore/contrib/io/factories.py +1 -1
- wbcore/contrib/io/import_export/backends/mail.py +1 -0
- wbcore/contrib/io/import_export/backends/sftp.py +29 -20
- wbcore/contrib/io/import_export/backends/stream.py +2 -2
- wbcore/contrib/io/import_export/parsers/__init__.py +0 -0
- wbcore/contrib/io/import_export/parsers/base_csv.py +36 -0
- wbcore/contrib/io/import_export/parsers/resources.py +50 -0
- wbcore/contrib/io/imports.py +33 -25
- wbcore/contrib/io/locale/de/LC_MESSAGES/django.po +114 -22
- wbcore/contrib/io/locale/de/LC_MESSAGES/django.po.translated +103 -0
- wbcore/contrib/io/locale/en/LC_MESSAGES/django.po +138 -0
- wbcore/contrib/io/locale/fr/LC_MESSAGES/django.po +138 -0
- wbcore/contrib/io/migrations/0008_importsource_resource_kwargs.py +18 -0
- wbcore/contrib/io/models.py +65 -45
- wbcore/contrib/io/resources.py +0 -6
- wbcore/contrib/io/serializers.py +2 -2
- wbcore/contrib/io/signals.py +4 -0
- wbcore/contrib/io/tests/test_backends.py +19 -13
- wbcore/contrib/io/tests/test_exports.py +1 -1
- wbcore/contrib/io/tests/test_imports.py +1 -1
- wbcore/contrib/io/tests/test_models.py +47 -14
- wbcore/contrib/io/tests/test_viewsets.py +271 -0
- wbcore/contrib/io/viewset_mixins.py +41 -54
- wbcore/contrib/notifications/admin.py +1 -0
- wbcore/contrib/notifications/apps.py +2 -1
- wbcore/contrib/notifications/backends/abstract_backend.py +2 -4
- wbcore/contrib/notifications/backends/firebase/backends.py +5 -2
- wbcore/contrib/notifications/dispatch.py +18 -7
- wbcore/contrib/notifications/factories/notification_types.py +1 -0
- wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po +25 -19
- wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po.translated +63 -0
- wbcore/contrib/notifications/locale/en/LC_MESSAGES/django.po +61 -0
- wbcore/contrib/notifications/locale/fr/LC_MESSAGES/django.po +62 -0
- wbcore/contrib/notifications/migrations/0008_notificationtype_is_lock.py +18 -0
- wbcore/contrib/notifications/migrations/0009_alter_notificationtypesetting_options_and_more.py +32 -0
- wbcore/contrib/notifications/models/notification_types.py +67 -24
- wbcore/contrib/notifications/serializers/notification_types.py +16 -1
- wbcore/contrib/notifications/tests/test_models/test_tokens.py +8 -0
- wbcore/contrib/notifications/tests/test_serializers/test_notification_types.py +5 -0
- wbcore/contrib/notifications/tests/test_viewsets/test_notification_types.py +3 -5
- wbcore/contrib/notifications/utils.py +3 -2
- wbcore/contrib/notifications/viewsets/configs/notification_types.py +28 -6
- wbcore/contrib/notifications/viewsets/menus.py +1 -1
- wbcore/contrib/notifications/viewsets/notification_types.py +12 -2
- wbcore/contrib/pandas/fields.py +38 -10
- wbcore/contrib/pandas/filters.py +4 -1
- wbcore/contrib/pandas/filterset.py +8 -7
- wbcore/contrib/pandas/tests/test_fields/test_number_fields.py +2 -7
- wbcore/contrib/pandas/utils.py +1 -1
- wbcore/contrib/pandas/views.py +14 -13
- wbcore/contrib/tags/models/tags.py +4 -1
- wbcore/contrib/workflow/factories/display.py +2 -2
- wbcore/contrib/workflow/factories/transition.py +16 -15
- wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po +457 -566
- wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po.translated +1326 -0
- wbcore/contrib/workflow/locale/en/LC_MESSAGES/django.po +1102 -0
- wbcore/contrib/workflow/locale/fr/LC_MESSAGES/django.po +1114 -0
- wbcore/contrib/workflow/models/data.py +7 -4
- wbcore/contrib/workflow/models/process.py +2 -2
- wbcore/contrib/workflow/models/step.py +57 -15
- wbcore/contrib/workflow/serializers/data.py +8 -8
- wbcore/contrib/workflow/serializers/process.py +3 -2
- wbcore/contrib/workflow/tests/conftest.py +224 -0
- wbcore/contrib/workflow/tests/test_dispatch.py +82 -77
- wbcore/contrib/workflow/tests/test_displays.py +10 -88
- wbcore/contrib/workflow/tests/test_filters.py +57 -40
- wbcore/contrib/workflow/tests/test_models/step/test_decision_step.py +71 -68
- wbcore/contrib/workflow/tests/test_models/step/test_email_step.py +78 -38
- wbcore/contrib/workflow/tests/test_models/step/test_finish_step.py +152 -90
- wbcore/contrib/workflow/tests/test_models/step/test_join_step.py +100 -110
- wbcore/contrib/workflow/tests/test_models/step/test_step.py +168 -33
- wbcore/contrib/workflow/tests/test_models/test_condition.py +1 -1
- wbcore/contrib/workflow/tests/test_models/test_workflow.py +3 -3
- wbcore/contrib/workflow/tests/test_serializers.py +172 -150
- wbcore/contrib/workflow/tests/test_viewsets.py +264 -323
- wbcore/contrib/workflow/tests/test_workflow_assignees.py +215 -205
- wbcore/contrib/workflow/viewsets/process.py +4 -1
- wbcore/contrib/workflow/workflows/assignees.py +12 -7
- wbcore/dynamic_preferences_registry.py +102 -0
- wbcore/enums.py +2 -51
- wbcore/filters/fields/choices.py +4 -6
- wbcore/filters/fields/content_type.py +15 -4
- wbcore/filters/fields/datetime.py +50 -25
- wbcore/filters/fields/models.py +18 -9
- wbcore/filters/fields/numbers.py +9 -8
- wbcore/filters/filterset.py +27 -6
- wbcore/filters/mixins.py +41 -42
- wbcore/forms.py +6 -6
- wbcore/fsm/markdown_extensions.py +1 -1
- wbcore/fsm/mixins.py +20 -6
- wbcore/locale/de/LC_MESSAGES/django.po +982 -397
- wbcore/locale/de/LC_MESSAGES/django.po.translated +1580 -0
- wbcore/locale/en/LC_MESSAGES/django.po +1234 -0
- wbcore/locale/fr/LC_MESSAGES/django.po +1235 -0
- wbcore/markdown/models.py +8 -5
- wbcore/markdown/views.py +1 -1
- wbcore/menus/menus.py +2 -2
- wbcore/metadata/configs/buttons/bases.py +10 -7
- wbcore/metadata/configs/buttons/buttons.py +2 -1
- wbcore/metadata/configs/buttons/enums.py +50 -0
- wbcore/metadata/configs/buttons/view_config.py +13 -46
- wbcore/metadata/configs/display/display.py +2 -2
- wbcore/metadata/configs/display/formatting.py +6 -9
- wbcore/metadata/configs/display/instance_display/display.py +5 -2
- wbcore/metadata/configs/display/instance_display/pages.py +1 -1
- wbcore/metadata/configs/display/instance_display/shortcuts.py +1 -1
- wbcore/metadata/configs/display/list_display.py +54 -40
- wbcore/metadata/configs/display/models.py +6 -0
- wbcore/metadata/configs/display/view_config.py +11 -9
- wbcore/metadata/configs/endpoints.py +11 -4
- wbcore/metadata/configs/fields.py +6 -1
- wbcore/metadata/configs/filter_fields.py +12 -13
- wbcore/metadata/configs/identifiers.py +3 -1
- wbcore/metadata/tests/test_buttons.py +13 -16
- wbcore/models/fields.py +2 -2
- wbcore/pagination.py +1 -2
- wbcore/permissions/permissions.py +2 -2
- wbcore/permissions/utils.py +2 -2
- wbcore/release_notes/display.py +2 -8
- wbcore/release_notes/serializers.py +2 -9
- wbcore/release_notes/viewsets.py +8 -2
- wbcore/reversion/viewsets/titles.py +4 -3
- wbcore/serializers/__init__.py +2 -0
- wbcore/serializers/fields/__init__.py +2 -1
- wbcore/serializers/fields/boolean.py +1 -1
- wbcore/serializers/fields/choice.py +28 -4
- wbcore/serializers/fields/datetime.py +45 -36
- wbcore/serializers/fields/fields.py +1 -1
- wbcore/serializers/fields/fsm.py +1 -1
- wbcore/serializers/fields/list.py +2 -5
- wbcore/serializers/fields/mixins.py +24 -11
- wbcore/serializers/fields/number.py +6 -23
- wbcore/serializers/fields/other.py +2 -10
- wbcore/serializers/fields/related.py +4 -6
- wbcore/serializers/fields/text.py +1 -1
- wbcore/serializers/fields/types.py +2 -0
- wbcore/serializers/serializers.py +12 -3
- wbcore/signals/__init__.py +1 -0
- wbcore/signals/clone.py +4 -0
- wbcore/signals/models.py +2 -6
- wbcore/tasks.py +2 -2
- wbcore/templates/wbcore/email_base_template.html +3 -3
- wbcore/test/e2e_helpers_methods/e2e_checks.py +10 -4
- wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +4 -2
- wbcore/test/mixins.py +52 -102
- wbcore/test/tests.py +6 -9
- wbcore/test/utils.py +3 -4
- wbcore/tests/e2e/test_e2e.py +2 -2
- wbcore/tests/test_cache/test_decorators.py +4 -7
- wbcore/tests/test_configs.py +2 -5
- wbcore/tests/test_enums.py +2 -1
- wbcore/tests/test_fields/test_choice_fields.py +9 -1
- wbcore/tests/test_fields/test_number_fields.py +7 -15
- wbcore/tests/test_fields/test_other_fields.py +1 -2
- wbcore/tests/test_filters/test_mixins.py +35 -35
- wbcore/tests/test_list_display.py +0 -2
- wbcore/tests/test_models/test_mixins.py +1 -1
- wbcore/tests/test_utils/test_date.py +1 -1
- wbcore/tests/test_utils/test_date_builder.py +25 -1
- wbcore/tests/test_utils/test_primary.py +1 -1
- wbcore/urls.py +4 -0
- wbcore/utils/date.py +18 -2
- wbcore/utils/figures.py +2 -2
- wbcore/utils/models.py +21 -4
- wbcore/utils/reportlab.py +7 -0
- wbcore/utils/rrules.py +3 -1
- wbcore/utils/string_loader.py +1 -1
- wbcore/utils/strings.py +3 -3
- wbcore/utils/views.py +8 -3
- wbcore/viewsets/mixins.py +9 -4
- {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/METADATA +9 -5
- {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/RECORD +317 -271
- wbcore/contrib/geography/tests/test_serializers.py +0 -7
- wbcore/contrib/geography/tests/tests.py +0 -13
- wbcore/contrib/io/tests/tests.py +0 -19
- wbcore/contrib/workflow/tests/tests.py +0 -25
- {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/WHEEL +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from unittest.mock import patch
|
|
2
|
-
|
|
3
1
|
import pytest
|
|
2
|
+
from pytest_mock import MockerFixture
|
|
4
3
|
|
|
5
|
-
from wbcore.contrib.authentication.
|
|
6
|
-
from wbcore.contrib.directory.
|
|
7
|
-
from wbcore.contrib.workflow.models import ProcessStep, Step
|
|
4
|
+
from wbcore.contrib.authentication.models import Group, User
|
|
5
|
+
from wbcore.contrib.directory.models import Entry
|
|
6
|
+
from wbcore.contrib.workflow.models import Process, ProcessStep, Step
|
|
8
7
|
from wbcore.contrib.workflow.workflows import (
|
|
9
8
|
manager_of_instance_assignee,
|
|
10
9
|
random_group_member,
|
|
@@ -12,215 +11,226 @@ from wbcore.contrib.workflow.workflows import (
|
|
|
12
11
|
)
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
@pytest.mark.django_db
|
|
16
14
|
class TestWorkflowAssignees:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
process_step = process_step_factory(process__instance=attached_instance)
|
|
21
|
-
manager_list = ClientFactory.create_batch(3, clients=[attached_instance.profile])
|
|
22
|
-
UserFactory(profile=manager_list[1], is_active=False)
|
|
23
|
-
UserFactory(profile=manager_list[2])
|
|
24
|
-
assert manager_of_instance_assignee(process_step, **kwargs) == manager_list[2].user_account
|
|
25
|
-
|
|
26
|
-
def test_manager_of_instance_assignee_no_kwargs(self, process_step_factory):
|
|
27
|
-
process_step = process_step_factory()
|
|
28
|
-
assert not manager_of_instance_assignee(process_step)
|
|
29
|
-
assert process_step.state == ProcessStep.StepState.FAILED
|
|
30
|
-
|
|
31
|
-
def test_manager_of_instance_assignee_no_assignee_field(self, process_step_factory):
|
|
32
|
-
kwargs = {"test": "profile", "assignee_type": "entry"}
|
|
33
|
-
process_step = process_step_factory()
|
|
34
|
-
assert not manager_of_instance_assignee(process_step, **kwargs)
|
|
35
|
-
assert process_step.state == ProcessStep.StepState.FAILED
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def mocked_active_user(self, mocker: MockerFixture):
|
|
17
|
+
return mocker.MagicMock(spec=User, is_active=True)
|
|
36
18
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
assert not manager_of_instance_assignee(process_step, **kwargs)
|
|
41
|
-
assert process_step.state == ProcessStep.StepState.FAILED
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def mocked_group(self, mocker: MockerFixture):
|
|
21
|
+
return mocker.MagicMock(spec=Group)
|
|
42
22
|
|
|
43
|
-
def
|
|
23
|
+
def test_manager_of_instance_assignee_person(self, mocker: MockerFixture, mocked_process_step, mocked_active_user):
|
|
24
|
+
# Arrange
|
|
44
25
|
kwargs = {"assignee_field": "profile", "assignee_type": "entry"}
|
|
45
|
-
process_step = process_step_factory()
|
|
46
|
-
assert not manager_of_instance_assignee(process_step, **kwargs)
|
|
47
|
-
assert process_step.state == ProcessStep.StepState.FAILED
|
|
48
26
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def test_weighted_random(self, mock_choices, user_step_factory, process_step_factory, bad_states, bad_types):
|
|
72
|
-
group = GroupFactory()
|
|
73
|
-
process_step = process_step_factory(group=group)
|
|
74
|
-
assignee1 = UserFactory(groups=[group])
|
|
75
|
-
assignee2 = UserFactory(groups=[group])
|
|
76
|
-
user_step = user_step_factory()
|
|
77
|
-
process_step_factory(
|
|
78
|
-
state=ProcessStep.StepState.ACTIVE,
|
|
79
|
-
process=process_step.process,
|
|
80
|
-
group=process_step.group,
|
|
81
|
-
step=user_step,
|
|
82
|
-
assignee=assignee1,
|
|
83
|
-
)
|
|
84
|
-
process_step_factory(
|
|
85
|
-
state=ProcessStep.StepState.FINISHED,
|
|
86
|
-
process=process_step.process,
|
|
87
|
-
group=process_step.group,
|
|
88
|
-
step=user_step,
|
|
89
|
-
assignee=assignee2,
|
|
90
|
-
)
|
|
91
|
-
process_step_factory(
|
|
92
|
-
state=ProcessStep.StepState.FINISHED,
|
|
93
|
-
process=process_step.process,
|
|
94
|
-
group=process_step.group,
|
|
95
|
-
step=user_step,
|
|
96
|
-
assignee=assignee2,
|
|
97
|
-
)
|
|
98
|
-
process_step_factory(
|
|
99
|
-
state=bad_states,
|
|
100
|
-
process=process_step.process,
|
|
101
|
-
group=process_step.group,
|
|
102
|
-
step=user_step,
|
|
103
|
-
assignee=assignee2,
|
|
104
|
-
)
|
|
105
|
-
process_step_factory(
|
|
106
|
-
state=ProcessStep.StepState.ACTIVE,
|
|
107
|
-
group=process_step.group,
|
|
108
|
-
step=user_step,
|
|
109
|
-
assignee=assignee2,
|
|
110
|
-
)
|
|
111
|
-
process_step_factory(
|
|
112
|
-
state=ProcessStep.StepState.ACTIVE,
|
|
113
|
-
process=process_step.process,
|
|
114
|
-
step=user_step,
|
|
115
|
-
assignee=assignee2,
|
|
116
|
-
)
|
|
117
|
-
process_step_factory(
|
|
118
|
-
state=ProcessStep.StepState.FINISHED,
|
|
119
|
-
process=process_step.process,
|
|
120
|
-
group=process_step.group,
|
|
121
|
-
step__step_type=bad_types,
|
|
122
|
-
assignee=assignee2,
|
|
27
|
+
user_a = mocker.MagicMock(spec=User, is_active=False)
|
|
28
|
+
manager_a = mocker.MagicMock(spec=Entry, user_account=user_a)
|
|
29
|
+
manager_b = mocker.MagicMock(spec=Entry, user_account=mocked_active_user)
|
|
30
|
+
|
|
31
|
+
assignee_mock = mocker.MagicMock()
|
|
32
|
+
|
|
33
|
+
mocked_process_step.process.instance = mocker.MagicMock(spec=User)
|
|
34
|
+
mocked_process_step.process.instance.profile = assignee_mock
|
|
35
|
+
|
|
36
|
+
assignee_mock.relationship_managers.all.return_value = [manager_a, manager_b]
|
|
37
|
+
# Act
|
|
38
|
+
result = manager_of_instance_assignee(mocked_process_step, **kwargs)
|
|
39
|
+
# Assert
|
|
40
|
+
assert result == manager_b.user_account
|
|
41
|
+
|
|
42
|
+
def test_manager_of_instance_assignee_no_kwargs(self, mocked_process_step, mocker: MockerFixture):
|
|
43
|
+
mocked_process_step.step.get_casted_step.return_value = mocker.MagicMock(spec=Step)
|
|
44
|
+
result = manager_of_instance_assignee(mocked_process_step)
|
|
45
|
+
assert result is None
|
|
46
|
+
mocked_process_step.step.get_casted_step().set_failed.assert_called_once_with(
|
|
47
|
+
mocked_process_step,
|
|
48
|
+
"Error in assignee method: Incorrect input in kwargs field!",
|
|
123
49
|
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
50
|
+
|
|
51
|
+
def test_manager_of_instance_assignee_no_assignee_field(self, mocked_process_step):
|
|
52
|
+
kwargs = {"test": "profile", "assignee_type": "entry"}
|
|
53
|
+
result = manager_of_instance_assignee(mocked_process_step, **kwargs)
|
|
54
|
+
assert result is None
|
|
55
|
+
mocked_process_step.step.get_casted_step().set_failed.assert_called_once_with(
|
|
56
|
+
mocked_process_step,
|
|
57
|
+
"Error in assignee method: Incorrect input in kwargs field!",
|
|
129
58
|
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
assert
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
)
|
|
139
|
-
@pytest.mark.parametrize(
|
|
140
|
-
"bad_types",
|
|
141
|
-
[
|
|
142
|
-
Step.StepType.DECISIONSTEP,
|
|
143
|
-
Step.StepType.EMAILSTEP,
|
|
144
|
-
Step.StepType.FINISHSTEP,
|
|
145
|
-
Step.StepType.JOINSTEP,
|
|
146
|
-
Step.StepType.SPLITSTEP,
|
|
147
|
-
Step.StepType.SCRIPTSTEP,
|
|
148
|
-
],
|
|
149
|
-
)
|
|
150
|
-
@patch("wbcore.contrib.workflow.workflows.assignees.choices")
|
|
151
|
-
def test_weighted_random_no_past_ocurrences(
|
|
152
|
-
self, mock_choices, user_step_factory, process_step_factory, bad_states, bad_types
|
|
153
|
-
):
|
|
154
|
-
group = GroupFactory()
|
|
155
|
-
process_step = process_step_factory(group=group)
|
|
156
|
-
assignee1 = UserFactory(groups=[group])
|
|
157
|
-
assignee2 = UserFactory(groups=[group])
|
|
158
|
-
user_step = user_step_factory()
|
|
159
|
-
|
|
160
|
-
process_step_factory(
|
|
161
|
-
state=bad_states,
|
|
162
|
-
process=process_step.process,
|
|
163
|
-
group=process_step.group,
|
|
164
|
-
step=user_step,
|
|
165
|
-
assignee=assignee2,
|
|
59
|
+
|
|
60
|
+
def test_manager_of_instance_assignee_no_assignee_type(self, mocked_process_step):
|
|
61
|
+
kwargs = {"assignee_field": "profile", "test": "entry"}
|
|
62
|
+
result = manager_of_instance_assignee(mocked_process_step, **kwargs)
|
|
63
|
+
assert result is None
|
|
64
|
+
mocked_process_step.step.get_casted_step().set_failed.assert_called_once_with(
|
|
65
|
+
mocked_process_step,
|
|
66
|
+
"Error in assignee method: Incorrect input in kwargs field!",
|
|
166
67
|
)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
68
|
+
|
|
69
|
+
def test_manager_of_instance_assignee_wrong_assignee_field(self, mocked_process_step):
|
|
70
|
+
kwargs = {"assignee_field": "profile", "assignee_type": "entry"}
|
|
71
|
+
result = manager_of_instance_assignee(mocked_process_step, **kwargs)
|
|
72
|
+
assert result is None
|
|
73
|
+
mocked_process_step.step.get_casted_step().set_failed.assert_called_once_with(
|
|
74
|
+
mocked_process_step,
|
|
75
|
+
"Error in assignee method: Incorrect input in kwargs field!",
|
|
172
76
|
)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
77
|
+
|
|
78
|
+
def test_manager_of_instance_assignee_wrong_assignee_type(self, mocked_process_step, mocked_active_user):
|
|
79
|
+
kwargs = {"assignee_field": "profile", "assignee_type": "test"}
|
|
80
|
+
mocked_process_step.process.instance = mocked_active_user
|
|
81
|
+
result = manager_of_instance_assignee(mocked_process_step, **kwargs)
|
|
82
|
+
assert result is None
|
|
83
|
+
mocked_process_step.step.get_casted_step().set_failed.assert_called_once_with(
|
|
84
|
+
mocked_process_step,
|
|
85
|
+
"Error in assignee method: Incorrect input in kwargs field!",
|
|
178
86
|
)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
87
|
+
|
|
88
|
+
def test_weighted_random_with_prior_assignments(self, mocker: MockerFixture, mocked_process_step, mocked_group):
|
|
89
|
+
# Arrange
|
|
90
|
+
user_a = mocker.MagicMock(spec=User, pk=1)
|
|
91
|
+
user_b = mocker.MagicMock(spec=User, pk=2)
|
|
92
|
+
mock_group_users = [user_a, user_b]
|
|
93
|
+
|
|
94
|
+
mocked_group.user_set.all.return_value = mock_group_users
|
|
95
|
+
mocked_group.user_set.count.return_value = len(mock_group_users)
|
|
96
|
+
|
|
97
|
+
mocked_process_step.group = mocked_group
|
|
98
|
+
mocked_process_step.process = mocker.MagicMock(spec=Process)
|
|
99
|
+
|
|
100
|
+
mocked_choices = mocker.patch("wbcore.contrib.workflow.workflows.assignees.choices", return_value=[user_a])
|
|
101
|
+
mocked_qs = mocker.patch("wbcore.contrib.workflow.workflows.assignees.ProcessStep.objects.filter")
|
|
102
|
+
|
|
103
|
+
mocked_query = mocked_qs.return_value
|
|
104
|
+
mocked_query.exclude.return_value.values_list.return_value = [1, 2, 2]
|
|
105
|
+
# Act
|
|
106
|
+
result = weighted_random(mocked_process_step)
|
|
107
|
+
|
|
108
|
+
mocked_choices.assert_called_once_with(mock_group_users, weights=mocker.ANY)
|
|
109
|
+
assignees, weights = (
|
|
110
|
+
mocked_choices.call_args.args[0],
|
|
111
|
+
mocked_choices.call_args.kwargs["weights"],
|
|
185
112
|
)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
113
|
+
# Assert
|
|
114
|
+
assert assignees == mock_group_users
|
|
115
|
+
assert weights == pytest.approx([2 / 3, 1 / 3], rel=1e-2)
|
|
116
|
+
assert result in mock_group_users
|
|
117
|
+
|
|
118
|
+
def test_weighted_random_no_group(self, mocker: MockerFixture, mocked_process_step):
|
|
119
|
+
# Arrange
|
|
120
|
+
mocked_process_step.group = None
|
|
121
|
+
mocked_failed_method = mocker.patch.object(mocked_process_step.step.get_casted_step(), "set_failed")
|
|
122
|
+
# Act
|
|
123
|
+
result = weighted_random(mocked_process_step)
|
|
124
|
+
# Assert
|
|
125
|
+
mocked_failed_method.assert_called_once()
|
|
126
|
+
assert result is None
|
|
127
|
+
|
|
128
|
+
def test_weighted_random_empty_group(self, mocker: MockerFixture, mocked_process_step, mocked_group):
|
|
129
|
+
# Arrange
|
|
130
|
+
mocked_group.user_set.count.return_value = 0
|
|
131
|
+
mocked_process_step.group = mocked_group
|
|
132
|
+
mocked_fail_method = mocker.patch.object(mocked_process_step.step.get_casted_step(), "set_failed")
|
|
133
|
+
# Act
|
|
134
|
+
result = weighted_random(mocked_process_step)
|
|
135
|
+
# Assert
|
|
136
|
+
mocked_fail_method.assert_called_once()
|
|
137
|
+
assert result is None
|
|
138
|
+
|
|
139
|
+
def test_weighted_random_no_prior_assignments(self, mocker: MockerFixture, mocked_process_step, mocked_group):
|
|
140
|
+
# Arrange
|
|
141
|
+
user_a = mocker.MagicMock(spec=User, pk=1)
|
|
142
|
+
user_b = mocker.MagicMock(spec=User, pk=2)
|
|
143
|
+
|
|
144
|
+
mock_group_users = [user_a, user_b]
|
|
145
|
+
mocked_group.user_set.all.return_value = mock_group_users = [user_a, user_b]
|
|
146
|
+
mocked_group.user_set.count.return_value = 2
|
|
147
|
+
mocked_process_step.group = mocked_group
|
|
148
|
+
|
|
149
|
+
mocked_choices = mocker.patch(
|
|
150
|
+
"wbcore.contrib.workflow.workflows.assignees.choices",
|
|
151
|
+
return_value=[user_b],
|
|
191
152
|
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
assert
|
|
217
|
-
|
|
218
|
-
def
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
153
|
+
mocked_qs = mocker.patch("wbcore.contrib.workflow.workflows.assignees.ProcessStep.objects.filter")
|
|
154
|
+
mocked_query = mocked_qs.return_value
|
|
155
|
+
mocked_query.exclude.return_value.values_list.return_value = []
|
|
156
|
+
# Act
|
|
157
|
+
result = weighted_random(mocked_process_step)
|
|
158
|
+
# Assert
|
|
159
|
+
mocked_choices.assert_called_once_with(mock_group_users)
|
|
160
|
+
assert result == user_b
|
|
161
|
+
|
|
162
|
+
def test_weighted_random_single_user(self, mocker: MockerFixture, mocked_process_step, mocked_group):
|
|
163
|
+
# Arrange
|
|
164
|
+
user_a = mocker.MagicMock(spec=User, pk=1)
|
|
165
|
+
mocked_group.user_set.all.return_value = [user_a]
|
|
166
|
+
mocked_group.user_set.count.return_value = 1
|
|
167
|
+
mocked_process_step.group = mocked_group
|
|
168
|
+
mocked_process_step.process = mocker.MagicMock(spec=Process)
|
|
169
|
+
|
|
170
|
+
mocked_qs = mocker.patch("wbcore.contrib.workflow.workflows.assignees.ProcessStep.objects.filter")
|
|
171
|
+
|
|
172
|
+
mocked_query = mocked_qs.return_value
|
|
173
|
+
mocked_query.exclude.return_value.values_list.return_value = []
|
|
174
|
+
# Act
|
|
175
|
+
result = weighted_random(mocked_process_step)
|
|
176
|
+
# Assert
|
|
177
|
+
assert result == user_a
|
|
178
|
+
|
|
179
|
+
def test_random_group_member_multiple_users(self, mocker: MockerFixture, mocked_process_step, mocked_group):
|
|
180
|
+
# Arrange
|
|
181
|
+
mocked_process_step = mocker.MagicMock(spec=ProcessStep)
|
|
182
|
+
|
|
183
|
+
user_a = mocker.MagicMock(spec=User)
|
|
184
|
+
user_b = mocker.MagicMock(spec=User)
|
|
185
|
+
user_c = mocker.MagicMock(spec=User)
|
|
186
|
+
users = [user_a, user_b, user_c]
|
|
187
|
+
|
|
188
|
+
mocked_group.user_set.exists.return_value = True
|
|
189
|
+
mocked_group.user_set.count.return_value = 3
|
|
190
|
+
mocked_group.user_set.all.return_value = users
|
|
191
|
+
|
|
192
|
+
mocked_process_step.group = mocked_group
|
|
193
|
+
|
|
194
|
+
mock_randint = mocker.patch("wbcore.contrib.workflow.workflows.assignees.randint", return_value=1)
|
|
195
|
+
# Act
|
|
196
|
+
result = random_group_member(mocked_process_step)
|
|
197
|
+
# Assert
|
|
198
|
+
mock_randint.assert_called_once_with(0, 2)
|
|
199
|
+
assert result == user_b # index 1
|
|
200
|
+
|
|
201
|
+
def test_random_group_member_single_user(self, mocker: MockerFixture, mocked_process_step, mocked_group):
|
|
202
|
+
# Arrange
|
|
203
|
+
mocked_process_step = mocker.MagicMock(spec=ProcessStep)
|
|
204
|
+
|
|
205
|
+
user_a = mocker.MagicMock(spec=User)
|
|
206
|
+
mocked_group.user_set.exists.return_value = True
|
|
207
|
+
mocked_group.user_set.count.return_value = 1
|
|
208
|
+
mocked_group.user_set.all.return_value = [user_a]
|
|
209
|
+
|
|
210
|
+
mocked_process_step.group = mocked_group
|
|
211
|
+
# Act
|
|
212
|
+
result = random_group_member(mocked_process_step)
|
|
213
|
+
# Assert
|
|
214
|
+
assert result == user_a
|
|
215
|
+
|
|
216
|
+
def test_random_group_member_empty_group(self, mocker, mocked_process_step, mocked_group):
|
|
217
|
+
# Arrange
|
|
218
|
+
mocked_process_step.group = mocked_group
|
|
219
|
+
mocked_process_step.group.user_set.exists.return_value = False
|
|
220
|
+
|
|
221
|
+
mocked_set_failed = mocker.patch.object(mocked_process_step.step.get_casted_step(), "set_failed")
|
|
222
|
+
# Act
|
|
223
|
+
result = random_group_member(mocked_process_step)
|
|
224
|
+
# Assert
|
|
225
|
+
mocked_set_failed.assert_called_once()
|
|
226
|
+
assert result is None
|
|
227
|
+
|
|
228
|
+
def test_random_group_member_no_group(self, mocker, mocked_process_step):
|
|
229
|
+
# Arrange
|
|
230
|
+
mocked_process_step.group = None
|
|
231
|
+
mocked_set_failed = mocker.patch.object(mocked_process_step.step.get_casted_step(), "set_failed")
|
|
232
|
+
# Act
|
|
233
|
+
result = random_group_member(mocked_process_step)
|
|
234
|
+
# Assert
|
|
235
|
+
mocked_set_failed.assert_called_once()
|
|
236
|
+
assert result is None
|
|
@@ -164,7 +164,10 @@ class AssignedProcessStepModelViewSet(ProcessStepModelViewSet):
|
|
|
164
164
|
)
|
|
165
165
|
)
|
|
166
166
|
)
|
|
167
|
-
.annotate(
|
|
167
|
+
.annotate(
|
|
168
|
+
attached_model=F("step__workflow__model__model"),
|
|
169
|
+
workflow_name=F("step__workflow__name"),
|
|
170
|
+
)
|
|
168
171
|
).select_related("step__workflow")
|
|
169
172
|
return qs
|
|
170
173
|
|
|
@@ -38,7 +38,10 @@ def weighted_random(process_step: ProcessStep, **kwargs) -> User | None:
|
|
|
38
38
|
if (group := process_step.group) and (group_user_count := group.user_set.count()):
|
|
39
39
|
similar_process_steps_selected_assignees: list[str] = list(
|
|
40
40
|
ProcessStep.objects.filter(
|
|
41
|
-
state__in=[
|
|
41
|
+
state__in=[
|
|
42
|
+
ProcessStep.StepState.FINISHED,
|
|
43
|
+
ProcessStep.StepState.ACTIVE,
|
|
44
|
+
],
|
|
42
45
|
process=process_step.process,
|
|
43
46
|
group=group,
|
|
44
47
|
assignee__isnull=False,
|
|
@@ -56,18 +59,19 @@ def weighted_random(process_step: ProcessStep, **kwargs) -> User | None:
|
|
|
56
59
|
# We redistribute each occurrence number between all of the other list items to increase their probability
|
|
57
60
|
redistributed_list: list[int] = [0 for i in range(group_user_count)]
|
|
58
61
|
for index, elem in enumerate(number_of_past_assignee_occurrences):
|
|
59
|
-
for index2
|
|
62
|
+
for index2 in range(len(redistributed_list)):
|
|
60
63
|
if not index2 == index:
|
|
61
64
|
redistributed_list[index2] += elem / (group_user_count - 1) if elem else 0
|
|
62
65
|
# Transform the list of absolute values into percentages
|
|
63
66
|
new_weights: list[float] = [x / sum(redistributed_list) for x in redistributed_list]
|
|
64
|
-
new_assignee: User = choices(group_member_list, weights=new_weights)[0]
|
|
67
|
+
new_assignee: User = choices(group_member_list, weights=new_weights)[0] # noqa
|
|
65
68
|
else:
|
|
66
|
-
new_assignee: User = choices(group_member_list)[0]
|
|
69
|
+
new_assignee: User = choices(group_member_list)[0] # noqa
|
|
67
70
|
return new_assignee
|
|
68
71
|
|
|
69
72
|
process_step.step.get_casted_step().set_failed(
|
|
70
|
-
process_step,
|
|
73
|
+
process_step,
|
|
74
|
+
_("Error in assignee method: No populated group to pick assignee from selected!"),
|
|
71
75
|
)
|
|
72
76
|
return None
|
|
73
77
|
|
|
@@ -75,9 +79,10 @@ def weighted_random(process_step: ProcessStep, **kwargs) -> User | None:
|
|
|
75
79
|
@register_assignee("Random Group Member")
|
|
76
80
|
def random_group_member(process_step: ProcessStep, **kwargs) -> User | None:
|
|
77
81
|
if (group := process_step.group) and group.user_set.exists():
|
|
78
|
-
return group.user_set.all()[randint(0, group.user_set.count() - 1)]
|
|
82
|
+
return group.user_set.all()[randint(0, group.user_set.count() - 1)] # noqa
|
|
79
83
|
|
|
80
84
|
process_step.step.get_casted_step().set_failed(
|
|
81
|
-
process_step,
|
|
85
|
+
process_step,
|
|
86
|
+
_("Error in assignee method: No populated group to pick assignee from selected!"),
|
|
82
87
|
)
|
|
83
88
|
return None
|
|
@@ -2,6 +2,10 @@ from django.utils.translation import gettext as _
|
|
|
2
2
|
from dynamic_preferences.preferences import Section
|
|
3
3
|
from dynamic_preferences.registries import global_preferences_registry
|
|
4
4
|
from dynamic_preferences.types import IntegerPreference, StringPreference
|
|
5
|
+
from dynamic_preferences.users.registries import user_preferences_registry
|
|
6
|
+
|
|
7
|
+
from wbcore.contrib.dynamic_preferences.types import ChoicePreference, LanguageChoicePreference
|
|
8
|
+
from wbcore.utils.date import get_timezone_choices
|
|
5
9
|
|
|
6
10
|
wbcore = Section("wbcore")
|
|
7
11
|
|
|
@@ -27,3 +31,101 @@ class SystemUserEmailPeriod(StringPreference):
|
|
|
27
31
|
|
|
28
32
|
verbose_name = _("System User Email")
|
|
29
33
|
help_text = _("System User Email")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@user_preferences_registry.register
|
|
37
|
+
class LanguagePreference(LanguageChoicePreference):
|
|
38
|
+
weight = -1
|
|
39
|
+
choices = [
|
|
40
|
+
("de", "Deutsch"),
|
|
41
|
+
("fr", "Français"),
|
|
42
|
+
("en", "English"),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
section = wbcore
|
|
46
|
+
name = "language"
|
|
47
|
+
default = "en"
|
|
48
|
+
|
|
49
|
+
verbose_name = _("System Language")
|
|
50
|
+
help_text = _("Select the language you want the Workbench to display.")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@user_preferences_registry.register
|
|
54
|
+
class TimezonePreference(ChoicePreference):
|
|
55
|
+
weight = 0
|
|
56
|
+
# Value is a IANA timezone name
|
|
57
|
+
choices = get_timezone_choices()
|
|
58
|
+
section = wbcore
|
|
59
|
+
name = "timezone"
|
|
60
|
+
default = "Europe/Berlin"
|
|
61
|
+
|
|
62
|
+
verbose_name = _("Timezone")
|
|
63
|
+
help_text = _("Pick the timezone in which you want the workbench's dates to be displayed in.")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@user_preferences_registry.register
|
|
67
|
+
class DateFormatPreference(ChoicePreference):
|
|
68
|
+
weight = 1
|
|
69
|
+
choices = [
|
|
70
|
+
("DD.MM.YYYY", "13.04.2007"),
|
|
71
|
+
("DD/MM/YYYY", "13/04/2007"),
|
|
72
|
+
("DD-MM-YYYY", "13-04-2007"),
|
|
73
|
+
("MM/DD/YYYY", "04/13/2007"),
|
|
74
|
+
("MM-DD-YYYY", "04-13-2007"),
|
|
75
|
+
("YYYY/MM/DD", "2007/04/13"),
|
|
76
|
+
("YYYY-MM-DD", "2007-04-13"),
|
|
77
|
+
("MMM DD, YYYY", "April 13, 2007"),
|
|
78
|
+
("DD MMM YYYY", "13 April 2007"),
|
|
79
|
+
("dddd, DD MMM YYYY", "Friday, 13 April 2007"),
|
|
80
|
+
("ddd, DD MMM YYYY", "Fri, 13 April 2007"),
|
|
81
|
+
("ddd Do MMMM YYYY", "Fri 13th April 2007"),
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
section = wbcore
|
|
85
|
+
name = "date_format"
|
|
86
|
+
default = "YYYY-MM-DD"
|
|
87
|
+
|
|
88
|
+
verbose_name = _("Date Format")
|
|
89
|
+
help_text = _("Choose how you want dates to appear throughout the Workbench.")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@user_preferences_registry.register
|
|
93
|
+
class TimeFormatPreference(ChoicePreference):
|
|
94
|
+
weight = 2
|
|
95
|
+
choices = [
|
|
96
|
+
("HH:mm", "14:05"),
|
|
97
|
+
("hh:mm", "02:05"),
|
|
98
|
+
("h:mm", "2:05"),
|
|
99
|
+
("HH [h] mm", "14 h 05"),
|
|
100
|
+
("HH[h]mm", "14h05"),
|
|
101
|
+
("h:mm A", "2:05 PM"),
|
|
102
|
+
("h:mm a", "2:05 pm"),
|
|
103
|
+
("hh:mm A", "02:05 PM"),
|
|
104
|
+
("hh:mm a", "02:05 pm"),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
section = wbcore
|
|
108
|
+
name = "time_format"
|
|
109
|
+
default = "HH:mm"
|
|
110
|
+
|
|
111
|
+
verbose_name = _("Time Format")
|
|
112
|
+
help_text = _("Choose how you want times to appear throughout the Workbench.")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@user_preferences_registry.register
|
|
116
|
+
class NumberFormatPreference(ChoicePreference):
|
|
117
|
+
weight = 3
|
|
118
|
+
# Value is a BCP 47 region subtag
|
|
119
|
+
choices = [
|
|
120
|
+
("US", "1,234,567.89"),
|
|
121
|
+
("FR", "1\u202f234\u202f567,89"),
|
|
122
|
+
("DE", "1.234.567,89"),
|
|
123
|
+
("CH", "1’234’567.89"),
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
section = wbcore
|
|
127
|
+
name = "number_format"
|
|
128
|
+
default = "US"
|
|
129
|
+
|
|
130
|
+
verbose_name = _("Number Format")
|
|
131
|
+
help_text = _("Choose how you want numbers to appear throughout the Workbench.")
|