wbcore 1.55.9__py2.py3-none-any.whl → 1.55.10rc0__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 +3 -3
- wbcore/cache/registry.py +1 -1
- wbcore/configs/decorators.py +1 -1
- wbcore/configurations/configurations/apps.py +2 -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/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 +1 -1
- wbcore/configurations/configurations/wbcore.py +1 -1
- wbcore/content_type/serializers.py +1 -1
- wbcore/content_type/utils.py +3 -3
- wbcore/contrib/agenda/viewsets/calendar_items.py +7 -7
- wbcore/contrib/ai/llm/config.py +1 -1
- wbcore/contrib/authentication/admin.py +2 -2
- 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/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 +1 -1
- wbcore/contrib/dataloader/utils.py +2 -2
- wbcore/contrib/directory/factories/__init__.py +1 -1
- wbcore/contrib/directory/factories/entries.py +1 -1
- wbcore/contrib/directory/models/contacts.py +1 -1
- wbcore/contrib/directory/models/relationships.py +7 -6
- wbcore/contrib/directory/serializers/contacts.py +8 -8
- wbcore/contrib/directory/serializers/entries.py +4 -4
- wbcore/contrib/directory/serializers/relationships.py +2 -2
- wbcore/contrib/directory/tests/disable_signals.py +11 -1
- wbcore/contrib/directory/tests/signals.py +2 -2
- wbcore/contrib/directory/tests/test_models.py +56 -37
- wbcore/contrib/directory/tests/test_serializers.py +1 -1
- wbcore/contrib/directory/tests/test_viewsets.py +8 -8
- wbcore/contrib/directory/viewsets/contacts.py +6 -6
- wbcore/contrib/directory/viewsets/display/__init__.py +1 -1
- wbcore/contrib/directory/viewsets/display/relationships.py +22 -22
- wbcore/contrib/directory/viewsets/previews/entries.py +3 -3
- wbcore/contrib/directory/viewsets/titles/relationships.py +2 -3
- wbcore/contrib/example_app/models.py +4 -4
- wbcore/contrib/example_app/serializers/person_team.py +4 -4
- wbcore/contrib/example_app/tests/e2e/test_teams.py +1 -1
- wbcore/contrib/geography/tests/test_viewsets.py +1 -1
- wbcore/contrib/guardian/tests/test_model_mixins.py +3 -3
- wbcore/contrib/guardian/tests/test_tasks.py +9 -9
- wbcore/contrib/guardian/tests/test_viewsets.py +2 -2
- wbcore/contrib/icons/icons.py +4 -8
- wbcore/contrib/io/import_export/backends/stream.py +2 -2
- wbcore/contrib/io/imports.py +1 -1
- wbcore/contrib/io/models.py +17 -14
- wbcore/contrib/io/serializers.py +2 -2
- wbcore/contrib/io/tests/test_backends.py +1 -1
- wbcore/contrib/io/tests/test_imports.py +1 -1
- wbcore/contrib/io/viewset_mixins.py +4 -4
- wbcore/contrib/notifications/dispatch.py +3 -1
- wbcore/contrib/pandas/filterset.py +8 -7
- wbcore/contrib/pandas/views.py +7 -5
- wbcore/contrib/tags/models/tags.py +4 -1
- wbcore/contrib/workflow/factories/display.py +2 -2
- wbcore/contrib/workflow/models/data.py +7 -4
- wbcore/contrib/workflow/models/process.py +2 -2
- wbcore/contrib/workflow/serializers/data.py +8 -8
- wbcore/contrib/workflow/tests/test_models/test_condition.py +1 -1
- wbcore/contrib/workflow/workflows/assignees.py +4 -4
- wbcore/enums.py +2 -1
- wbcore/filters/fields/datetime.py +1 -1
- wbcore/filters/fields/models.py +2 -2
- wbcore/filters/filterset.py +4 -4
- wbcore/forms.py +4 -4
- wbcore/fsm/markdown_extensions.py +1 -1
- wbcore/fsm/mixins.py +3 -3
- wbcore/markdown/models.py +8 -5
- wbcore/metadata/configs/buttons/bases.py +6 -6
- wbcore/metadata/configs/buttons/buttons.py +2 -1
- wbcore/metadata/configs/buttons/view_config.py +5 -3
- wbcore/metadata/configs/display/display.py +2 -2
- wbcore/metadata/configs/display/formatting.py +6 -7
- wbcore/metadata/configs/display/list_display.py +2 -4
- wbcore/metadata/configs/display/models.py +6 -0
- wbcore/models/fields.py +2 -2
- wbcore/permissions/utils.py +2 -2
- wbcore/reversion/viewsets/titles.py +4 -3
- wbcore/serializers/fields/datetime.py +3 -3
- wbcore/serializers/fields/fields.py +1 -1
- wbcore/serializers/fields/fsm.py +1 -1
- wbcore/serializers/fields/list.py +1 -1
- wbcore/serializers/fields/mixins.py +5 -5
- wbcore/serializers/fields/related.py +3 -5
- wbcore/serializers/fields/text.py +1 -1
- wbcore/serializers/serializers.py +2 -2
- wbcore/tasks.py +2 -2
- wbcore/test/e2e_helpers_methods/e2e_checks.py +10 -4
- wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +4 -2
- wbcore/test/tests.py +6 -9
- wbcore/test/utils.py +2 -3
- wbcore/tests/e2e/test_e2e.py +2 -2
- wbcore/tests/test_cache/test_decorators.py +3 -3
- wbcore/tests/test_configs.py +1 -1
- wbcore/tests/test_fields/test_number_fields.py +1 -1
- wbcore/tests/test_filters/test_mixins.py +3 -3
- 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/utils/date.py +4 -1
- wbcore/utils/figures.py +2 -2
- wbcore/utils/models.py +3 -2
- wbcore/utils/string_loader.py +1 -1
- wbcore/viewsets/mixins.py +6 -4
- {wbcore-1.55.9.dist-info → wbcore-1.55.10rc0.dist-info}/METADATA +1 -1
- {wbcore-1.55.9.dist-info → wbcore-1.55.10rc0.dist-info}/RECORD +119 -119
- {wbcore-1.55.9.dist-info → wbcore-1.55.10rc0.dist-info}/WHEEL +0 -0
wbcore/cache/decorators.py
CHANGED
|
@@ -15,9 +15,9 @@ def cache_table(
|
|
|
15
15
|
periodic_caching_get_parameters: list[dict[str, str]] | Callable | None = None,
|
|
16
16
|
):
|
|
17
17
|
def _decorator(pandas_view_class):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
pandas_view_class.CACHE_ENABLED = not settings.DEBUG
|
|
19
|
+
pandas_view_class.CACHE_TIMEOUT = timeout
|
|
20
|
+
pandas_view_class.CACHE_KEY_PREFIX = key_prefix
|
|
21
21
|
add_extra_button.connect(add_clear_cache_button, sender=pandas_view_class, weak=False)
|
|
22
22
|
|
|
23
23
|
if periodic_caching:
|
wbcore/cache/registry.py
CHANGED
|
@@ -62,7 +62,7 @@ class CachedClass:
|
|
|
62
62
|
for request in self._get_requests(**kwargs):
|
|
63
63
|
with suppress(RequiredFilterMissing):
|
|
64
64
|
view = self.view_class(request=request, kwargs=kwargs)
|
|
65
|
-
|
|
65
|
+
request.parser_context = {"request": request, "view": view, "kwargs": kwargs}
|
|
66
66
|
cache_key = view._get_cache_key()
|
|
67
67
|
cache.delete(cache_key)
|
|
68
68
|
res.append(view._get_dataframe())
|
wbcore/configs/decorators.py
CHANGED
|
@@ -39,13 +39,13 @@ class Apps:
|
|
|
39
39
|
return getattr(self, "ADDITIONAL_APPS", []) + getattr(self, "WB_ENDPOINTS", [])
|
|
40
40
|
|
|
41
41
|
@property
|
|
42
|
-
def INSTALLED_APPS(self):
|
|
42
|
+
def INSTALLED_APPS(self): # noqa
|
|
43
43
|
return self._BASE_APPS + self._get_additional_and_wb_apps()
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
class DevApps(Apps):
|
|
47
47
|
@property
|
|
48
|
-
def INSTALLED_APPS(self):
|
|
48
|
+
def INSTALLED_APPS(self): # noqa
|
|
49
49
|
apps = self._BASE_APPS
|
|
50
50
|
|
|
51
51
|
if self.DEBUG:
|
|
@@ -24,7 +24,7 @@ class Authentication:
|
|
|
24
24
|
JWT_COOKIE_KEY = values.Value("JWT-access", environ_prefix=None)
|
|
25
25
|
|
|
26
26
|
@property
|
|
27
|
-
def SIMPLE_JWT(self):
|
|
27
|
+
def SIMPLE_JWT(self): # noqa
|
|
28
28
|
return {
|
|
29
29
|
"ACCESS_TOKEN_LIFETIME": timedelta(seconds=self.JWT_ACCESS_TOKEN_LIFETIME),
|
|
30
30
|
"REFRESH_TOKEN_LIFETIME": timedelta(seconds=self.JWT_REFRESH_TOKEN_LIFETIME),
|
|
@@ -9,7 +9,7 @@ class Base:
|
|
|
9
9
|
SITE_ID = values.IntegerValue(1, environ_prefix=None)
|
|
10
10
|
|
|
11
11
|
@property
|
|
12
|
-
def PROJECT_NAME(self):
|
|
12
|
+
def PROJECT_NAME(self): # noqa
|
|
13
13
|
if settings_module := os.environ.get("DJANGO_SETTINGS_MODULE", None):
|
|
14
14
|
return settings_module.split(".")[0]
|
|
15
15
|
return None
|
|
@@ -6,7 +6,7 @@ class Maintenance:
|
|
|
6
6
|
MAINTENANCE_MODE_STR: str | None = values.Value("False", environ_prefix=None)
|
|
7
7
|
|
|
8
8
|
# We allow maintenance mode to be either True, False or None. In case of None, the specified Backend will take over.
|
|
9
|
-
def MAINTENANCE_MODE(self) -> bool | None:
|
|
9
|
+
def MAINTENANCE_MODE(self) -> bool | None: # noqa
|
|
10
10
|
if value := self.MAINTENANCE_MODE_STR:
|
|
11
11
|
normalized_value = value.strip().lower()
|
|
12
12
|
if normalized_value in values.BooleanValue.true_values:
|
|
@@ -6,7 +6,7 @@ class Restframework:
|
|
|
6
6
|
REST_FRAMEWORK_THROTTLE_PERIOD = values.Value("hour", environ_prefix=None)
|
|
7
7
|
REST_FRAMEWORK_USER_THROTTLE_RATES = values.IntegerValue(5000, environ_prefix=None)
|
|
8
8
|
|
|
9
|
-
def REST_FRAMEWORK(self):
|
|
9
|
+
def REST_FRAMEWORK(self): # noqa
|
|
10
10
|
rest_framework = {
|
|
11
11
|
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
|
|
12
12
|
"PAGE_SIZE": 25,
|
|
@@ -46,7 +46,7 @@ class WBCore:
|
|
|
46
46
|
MESSAGE_STORAGE = "wbcore.messages.route_message_storage"
|
|
47
47
|
|
|
48
48
|
@property
|
|
49
|
-
def FRONTEND_CONTEXT(self):
|
|
49
|
+
def FRONTEND_CONTEXT(self): # noqa
|
|
50
50
|
base_url = f"{self.CDN_BASE_ENDPOINT_URL}/{self.FRONTEND_VERSION}/"
|
|
51
51
|
return {
|
|
52
52
|
"TITLE": "Workbench",
|
|
@@ -54,7 +54,7 @@ class ContentTypeRepresentationSerializer(serializers.RepresentationSerializer):
|
|
|
54
54
|
fields = ("id", "app_label", "model", "model_title")
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
class GenericModel(models.Model):
|
|
57
|
+
class GenericModel(models.Model): # noqa
|
|
58
58
|
id = models.IntegerField(primary_key=True)
|
|
59
59
|
label = models.CharField(max_length=256)
|
|
60
60
|
|
wbcore/content_type/utils.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from contextlib import suppress
|
|
2
|
+
|
|
1
3
|
from django.contrib.contenttypes.models import ContentType
|
|
2
4
|
|
|
3
5
|
|
|
@@ -17,12 +19,10 @@ def get_ancestors_content_type(content_type):
|
|
|
17
19
|
]
|
|
18
20
|
"""
|
|
19
21
|
for _class in content_type.model_class().__mro__:
|
|
20
|
-
|
|
22
|
+
with suppress(Exception):
|
|
21
23
|
ancestor_content_type = ContentType.objects.get_for_model(_class)
|
|
22
24
|
if ancestor_content_type.model_class():
|
|
23
25
|
yield ancestor_content_type
|
|
24
|
-
except Exception:
|
|
25
|
-
pass
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def get_view_content_type_id(view):
|
|
@@ -38,13 +38,13 @@ class CalendarItemRepresentationViewSet(viewsets.RepresentationViewSet):
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class CalendarItemMetaClass(type(viewsets.ModelViewSet)):
|
|
41
|
-
def __new__(
|
|
42
|
-
_class = super().__new__(
|
|
41
|
+
def __new__(cls, *args, **kwargs):
|
|
42
|
+
_class = super().__new__(cls, *args, **kwargs)
|
|
43
43
|
dependant_identifiers = []
|
|
44
44
|
for subclass in get_inheriting_subclasses(_class):
|
|
45
45
|
if identifier := getattr(subclass, "IDENTIFIER", None):
|
|
46
46
|
dependant_identifiers.append(identifier)
|
|
47
|
-
|
|
47
|
+
_class.DEPENDANT_IDENTIFIERS = dependant_identifiers
|
|
48
48
|
return _class
|
|
49
49
|
|
|
50
50
|
|
|
@@ -68,7 +68,7 @@ class CalendarItemViewSet(viewsets.ModelViewSet, metaclass=CalendarItemMetaClass
|
|
|
68
68
|
Property to store the unioned queryset of draggable calendar items. Each module inheriting from CalendarItem are expected to define the signal receiver
|
|
69
69
|
"""
|
|
70
70
|
qs = CalendarItem.objects.none()
|
|
71
|
-
for
|
|
71
|
+
for _, union_qs in draggable_calendar_item_ids.send(CalendarItem, request=self.request):
|
|
72
72
|
qs = qs.union(union_qs)
|
|
73
73
|
return qs
|
|
74
74
|
|
|
@@ -154,15 +154,15 @@ def get_ics(request):
|
|
|
154
154
|
min_max_range = TimestamptzRange(minimum_date, maximum_date)
|
|
155
155
|
gen = qs.filter(period__contained_by=min_max_range)
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
ical = Calendar()
|
|
158
158
|
for occurence in gen:
|
|
159
159
|
start = occurence.period.lower
|
|
160
160
|
end = occurence.period.upper
|
|
161
161
|
activity = occurence
|
|
162
162
|
event = activity.to_ics(start, end)
|
|
163
163
|
if event:
|
|
164
|
-
|
|
165
|
-
data = StringIO(str(
|
|
164
|
+
ical.events.add(event)
|
|
165
|
+
data = StringIO(str(ical))
|
|
166
166
|
response = HttpResponse(data, content_type="text/calendar")
|
|
167
167
|
response["Content-Disposition"] = f'attachment; filename="{profile.computed_str}.ics"'
|
|
168
168
|
return response
|
wbcore/contrib/ai/llm/config.py
CHANGED
|
@@ -7,12 +7,12 @@ from django.db.models.signals import ModelSignal
|
|
|
7
7
|
from langchain_core.language_models import BaseChatModel
|
|
8
8
|
from langchain_core.messages import BaseMessage
|
|
9
9
|
from langchain_openai import ChatOpenAI
|
|
10
|
+
from pydantic import BaseModel
|
|
10
11
|
|
|
11
12
|
from ..exceptions import APIStatusErrors, BadRequestErrors
|
|
12
13
|
from .utils import run_llm
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger("llm")
|
|
15
|
-
from pydantic import BaseModel
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@shared_task(
|
|
@@ -202,11 +202,11 @@ class UserAdmin(admin.ModelAdmin):
|
|
|
202
202
|
form = self.change_password_form(user)
|
|
203
203
|
|
|
204
204
|
fieldsets = [(None, {"fields": list(form.base_fields)})]
|
|
205
|
-
|
|
205
|
+
admin_form = admin.helpers.AdminForm(form, fieldsets, {})
|
|
206
206
|
|
|
207
207
|
context = {
|
|
208
208
|
"title": _("Change password: %s") % escape(user.get_username()),
|
|
209
|
-
"adminForm":
|
|
209
|
+
"adminForm": admin_form,
|
|
210
210
|
"form_url": form_url,
|
|
211
211
|
"form": form,
|
|
212
212
|
"is_popup": (IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET),
|
|
@@ -166,7 +166,7 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|
|
166
166
|
return self.profile.last_name
|
|
167
167
|
|
|
168
168
|
@classmethod
|
|
169
|
-
def get_endpoint_basename(
|
|
169
|
+
def get_endpoint_basename(cls):
|
|
170
170
|
return "wbcore:authentication:user"
|
|
171
171
|
|
|
172
172
|
@classmethod
|
|
@@ -187,7 +187,7 @@ class Group(DjangoBaseGroup):
|
|
|
187
187
|
proxy = True
|
|
188
188
|
|
|
189
189
|
@classmethod
|
|
190
|
-
def get_endpoint_basename(
|
|
190
|
+
def get_endpoint_basename(cls):
|
|
191
191
|
return "wbcore:authentication:group"
|
|
192
192
|
|
|
193
193
|
@classmethod
|
|
@@ -208,7 +208,7 @@ class Permission(DjangoBasePermission):
|
|
|
208
208
|
proxy = True
|
|
209
209
|
|
|
210
210
|
@classmethod
|
|
211
|
-
def get_endpoint_basename(
|
|
211
|
+
def get_endpoint_basename(cls):
|
|
212
212
|
return "wbcore:authentication:permission"
|
|
213
213
|
|
|
214
214
|
@classmethod
|
|
@@ -59,7 +59,7 @@ class UserActivity(models.Model):
|
|
|
59
59
|
return Subquery(qs.order_by("-latest_refresh").values("latest_refresh")[:1], output_field=DateTimeField())
|
|
60
60
|
|
|
61
61
|
@classmethod
|
|
62
|
-
def get_endpoint_basename(
|
|
62
|
+
def get_endpoint_basename(cls):
|
|
63
63
|
return "wbcore:authentication:useractivity"
|
|
64
64
|
|
|
65
65
|
@classmethod
|
|
@@ -60,7 +60,7 @@ class ChangePasswordSerializer(wb_serializers.Serializer):
|
|
|
60
60
|
validate_password(new_password)
|
|
61
61
|
return super().validate(data)
|
|
62
62
|
except serializers.ValidationError as e:
|
|
63
|
-
raise serializers.ValidationError({"new_password": e, "confirm_password": e})
|
|
63
|
+
raise serializers.ValidationError({"new_password": e, "confirm_password": e}) from e
|
|
64
64
|
|
|
65
65
|
class Meta:
|
|
66
66
|
fields = ("current_password", "new_password", "confirm_password")
|
|
@@ -336,7 +336,7 @@ class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
|
|
|
336
336
|
raise exceptions.AuthenticationFailed(
|
|
337
337
|
self.error_messages["no_active_account"],
|
|
338
338
|
"no_active_account",
|
|
339
|
-
)
|
|
339
|
+
) from None
|
|
340
340
|
return data
|
|
341
341
|
|
|
342
342
|
|
|
@@ -9,7 +9,7 @@ from rest_framework import exceptions
|
|
|
9
9
|
from rest_framework.test import APIRequestFactory
|
|
10
10
|
|
|
11
11
|
from wbcore.contrib.authentication.authentication import inject_short_lived_token
|
|
12
|
-
from wbcore.contrib.authentication.models import Token
|
|
12
|
+
from wbcore.contrib.authentication.models import Token, User
|
|
13
13
|
|
|
14
14
|
fake = Faker()
|
|
15
15
|
now = datetime.now()
|
|
@@ -17,9 +17,9 @@ now = datetime.now()
|
|
|
17
17
|
|
|
18
18
|
class TestTokenUnitTests:
|
|
19
19
|
@pytest.fixture
|
|
20
|
-
def request_with_user(
|
|
20
|
+
def request_with_user(self: User):
|
|
21
21
|
request = APIRequestFactory().get("/")
|
|
22
|
-
request.user =
|
|
22
|
+
request.user = self
|
|
23
23
|
return request
|
|
24
24
|
|
|
25
25
|
def setup_method(self):
|
|
@@ -155,7 +155,6 @@ class TestRegistrationAndActivationViews:
|
|
|
155
155
|
# Arrange
|
|
156
156
|
mock_get_user = mocker.patch(
|
|
157
157
|
"wbcore.contrib.authentication.models.users.UserManager.get",
|
|
158
|
-
side_effect=Exception if should_fail else None,
|
|
159
158
|
return_value=None if should_fail else test_user,
|
|
160
159
|
)
|
|
161
160
|
mock_reset_password = mocker.patch.object(test_user, "reset_password")
|
|
@@ -157,7 +157,8 @@ class UserActivityChart(viewsets.ChartViewSet):
|
|
|
157
157
|
cum_times = pd.Series(
|
|
158
158
|
dt
|
|
159
159
|
for group in [
|
|
160
|
-
pd.date_range(start, end, freq="Min")
|
|
160
|
+
pd.date_range(start, end, freq="Min")
|
|
161
|
+
for start, end in zip(dff.date, dff.latest_refresh, strict=False)
|
|
161
162
|
]
|
|
162
163
|
for dt in group
|
|
163
164
|
).value_counts()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from contextlib import suppress
|
|
2
3
|
|
|
3
4
|
from django.conf import settings
|
|
4
5
|
from django.contrib.auth.models import Group, Permission
|
|
@@ -66,11 +67,12 @@ logger = logging.getLogger()
|
|
|
66
67
|
@permission_classes([AllowAny])
|
|
67
68
|
@authentication_classes([])
|
|
68
69
|
def reset_password_email(request):
|
|
69
|
-
|
|
70
|
+
with suppress(User.DoesNotExist):
|
|
70
71
|
user = User.objects.get(email=request.data["email"])
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
try:
|
|
73
|
+
user.reset_password(request)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(f"While user {user} try to reset password, we encounter the error {e}")
|
|
74
76
|
return Response(
|
|
75
77
|
{
|
|
76
78
|
"status": "ok",
|
wbcore/contrib/color/models.py
CHANGED
|
@@ -39,7 +39,8 @@ class ColorGradient(models.Model):
|
|
|
39
39
|
if color:
|
|
40
40
|
color_rgb = _hex2rgb(color)
|
|
41
41
|
nearest_color = min(
|
|
42
|
-
self.colors,
|
|
42
|
+
self.colors,
|
|
43
|
+
key=lambda subject: sum((s - q) ** 2 for s, q in zip(_hex2rgb(subject), color_rgb, strict=False)),
|
|
43
44
|
)
|
|
44
45
|
|
|
45
46
|
index = self.colors.index(nearest_color)
|
|
@@ -6,7 +6,7 @@ import yaml
|
|
|
6
6
|
from .models import Currency, CurrencyFXRates
|
|
7
7
|
|
|
8
8
|
with open(pathlib.Path(__file__).parent.joinpath("fixtures").joinpath("currency.yaml"), "r") as yaml_file:
|
|
9
|
-
currency_dict = yaml.load(yaml_file, Loader=yaml.CLoader)
|
|
9
|
+
currency_dict = yaml.load(yaml_file, Loader=yaml.CLoader) # noqa: S506
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class CurrencyFactory(factory.django.DjangoModelFactory):
|
|
@@ -55,7 +55,9 @@ class DataBackend(AbstractDataBackend):
|
|
|
55
55
|
params["symbols"] = ",".join(obj_external_ids)
|
|
56
56
|
res = []
|
|
57
57
|
for _date in pd.date_range(start, execution_date, freq="B"):
|
|
58
|
-
r = requests.get(
|
|
58
|
+
r = requests.get(
|
|
59
|
+
f'{self.ENDPOINT}/{_date.strftime("%Y-%m-%d")}?', params=params, headers=self.HEADERS, timeout=10
|
|
60
|
+
)
|
|
59
61
|
if r.status_code == requests.codes.ok:
|
|
60
62
|
res_json = r.json()
|
|
61
63
|
if res_json and res_json["success"]:
|
|
@@ -67,7 +67,7 @@ class Currency(ImportMixin, LabelKeyMixin, WBModel):
|
|
|
67
67
|
except CurrencyFXRates.DoesNotExist:
|
|
68
68
|
if exact_lookup:
|
|
69
69
|
return self.convert(valuation_date, other_currency, exact_lookup=False)
|
|
70
|
-
raise CurrencyFXRates.DoesNotExist
|
|
70
|
+
raise CurrencyFXRates.DoesNotExist from None
|
|
71
71
|
|
|
72
72
|
@classmethod
|
|
73
73
|
def get_endpoint_basename(cls) -> str:
|
|
@@ -12,9 +12,9 @@ def dictfetchall[T](cursor: "CursorWrapper", dict_type: type[T] = dict) -> Itera
|
|
|
12
12
|
columns = get_columns(cursor)
|
|
13
13
|
for row in cursor.fetchall():
|
|
14
14
|
# The spec for TypedDict is not compliant with the sepc for dict
|
|
15
|
-
yield dict_type(zip(columns, row)) # type: ignore
|
|
15
|
+
yield dict_type(zip(columns, row, strict=False)) # type: ignore
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def dictfetchone(cursor: "CursorWrapper") -> dict:
|
|
19
19
|
columns = get_columns(cursor)
|
|
20
|
-
return dict(zip(columns, cursor.fetchone() or []))
|
|
20
|
+
return dict(zip(columns, cursor.fetchone() or [], strict=False))
|
|
@@ -158,7 +158,7 @@ class ClientFactory(PersonFactory):
|
|
|
158
158
|
self.clients.add(person)
|
|
159
159
|
|
|
160
160
|
|
|
161
|
-
class
|
|
161
|
+
class RandomClientFactory(ClientFactory):
|
|
162
162
|
@factory.post_generation
|
|
163
163
|
def clients(self, create, extracted, **kwargs):
|
|
164
164
|
self.clients.add(PersonFactory())
|
|
@@ -40,7 +40,7 @@ class BankingContact(PrimaryMixin, WBModel):
|
|
|
40
40
|
@classmethod
|
|
41
41
|
def get_color_map(cls):
|
|
42
42
|
colors = [WBColor.RED_LIGHT.value, WBColor.YELLOW_LIGHT.value, WBColor.GREEN_LIGHT.value]
|
|
43
|
-
return [choice for choice in zip(cls, colors)]
|
|
43
|
+
return [choice for choice in zip(cls, colors, strict=False)]
|
|
44
44
|
|
|
45
45
|
status = FSMField(
|
|
46
46
|
default=Status.DRAFT,
|
|
@@ -307,8 +307,8 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
|
|
|
307
307
|
def approveremoval(self, by=None, description=None, **kwargs):
|
|
308
308
|
pass
|
|
309
309
|
|
|
310
|
-
def is_not_primary(
|
|
311
|
-
return not
|
|
310
|
+
def is_not_primary(self):
|
|
311
|
+
return not self.primary
|
|
312
312
|
|
|
313
313
|
@transition(
|
|
314
314
|
field=status,
|
|
@@ -331,10 +331,10 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
|
|
|
331
331
|
def makeprimary(self, by=None, description=None, **kwargs):
|
|
332
332
|
self.primary = True
|
|
333
333
|
|
|
334
|
-
def last_primary(
|
|
335
|
-
return not
|
|
336
|
-
ClientManagerRelationship.objects.exclude(id=
|
|
337
|
-
.filter(status=ClientManagerRelationship.Status.APPROVED, client=
|
|
334
|
+
def last_primary(self):
|
|
335
|
+
return not self.primary and (
|
|
336
|
+
ClientManagerRelationship.objects.exclude(id=self.id)
|
|
337
|
+
.filter(status=ClientManagerRelationship.Status.APPROVED, client=self.client, primary=True)
|
|
338
338
|
.exists()
|
|
339
339
|
)
|
|
340
340
|
|
|
@@ -555,6 +555,7 @@ class EmployerEmployeeRelationship(PrimaryMixin):
|
|
|
555
555
|
def get_representation_value_key(cls):
|
|
556
556
|
return "id"
|
|
557
557
|
|
|
558
|
+
@classmethod
|
|
558
559
|
def representation_label_key(cls):
|
|
559
560
|
return "{{position_name}}"
|
|
560
561
|
|
|
@@ -126,8 +126,8 @@ class EmailContactSerializer(wb_serializers.ModelSerializer):
|
|
|
126
126
|
if address:
|
|
127
127
|
try:
|
|
128
128
|
validate_email(address)
|
|
129
|
-
except ValidationError:
|
|
130
|
-
raise serializers.ValidationError({"address": _("Invalid e-mail address")})
|
|
129
|
+
except ValidationError as e:
|
|
130
|
+
raise serializers.ValidationError({"address": _("Invalid e-mail address")}) from e
|
|
131
131
|
return data
|
|
132
132
|
|
|
133
133
|
class Meta:
|
|
@@ -261,8 +261,8 @@ class TelephoneContactSerializer(wb_serializers.ModelSerializer):
|
|
|
261
261
|
data["number"] = formatted_number
|
|
262
262
|
else:
|
|
263
263
|
raise serializers.ValidationError({"number": _("Invalid phone number format")})
|
|
264
|
-
except Exception:
|
|
265
|
-
raise serializers.ValidationError({"number": _("Invalid phone number format")})
|
|
264
|
+
except Exception as e:
|
|
265
|
+
raise serializers.ValidationError({"number": _("Invalid phone number format")}) from e
|
|
266
266
|
|
|
267
267
|
if entry and formatting_successful:
|
|
268
268
|
telephone_contact = TelephoneContact.objects.filter(number=formatted_number, entry=entry)
|
|
@@ -310,7 +310,7 @@ class BankingContactSerializer(wb_serializers.ModelSerializer):
|
|
|
310
310
|
iban_formatted = iban.formatted
|
|
311
311
|
data["iban"] = iban_formatted
|
|
312
312
|
except ValueError as e:
|
|
313
|
-
raise serializers.ValidationError({"iban": e})
|
|
313
|
+
raise serializers.ValidationError({"iban": e}) from e
|
|
314
314
|
|
|
315
315
|
if entry and iban_formatted:
|
|
316
316
|
banking_contact = BankingContact.objects.filter(iban=iban_formatted, entry=entry)
|
|
@@ -325,7 +325,7 @@ class BankingContactSerializer(wb_serializers.ModelSerializer):
|
|
|
325
325
|
try:
|
|
326
326
|
BIC(swift_bic_repr)
|
|
327
327
|
except ValueError as e:
|
|
328
|
-
raise serializers.ValidationError({"swift_bic": e})
|
|
328
|
+
raise serializers.ValidationError({"swift_bic": e}) from e
|
|
329
329
|
return data
|
|
330
330
|
|
|
331
331
|
class Meta:
|
|
@@ -364,8 +364,8 @@ class SocialMediaContactSerializer(wb_serializers.ModelSerializer):
|
|
|
364
364
|
url = data.get("url", self.instance.url if self.instance else "")
|
|
365
365
|
try:
|
|
366
366
|
URLValidator()(url)
|
|
367
|
-
except ValidationError:
|
|
368
|
-
raise serializers.ValidationError({"url": _("The URL you provided seems to be wrong.")})
|
|
367
|
+
except ValidationError as e:
|
|
368
|
+
raise serializers.ValidationError({"url": _("The URL you provided seems to be wrong.")}) from e
|
|
369
369
|
|
|
370
370
|
if self.instance and (
|
|
371
371
|
((data_entry := data.get("entry")) != self.instance.entry)
|
|
@@ -252,8 +252,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
|
|
|
252
252
|
if primary_email := data.get("primary_email", None):
|
|
253
253
|
try:
|
|
254
254
|
validate_email(primary_email)
|
|
255
|
-
except ValidationError:
|
|
256
|
-
raise ValidationError({"primary_email": "Invalid e-mail address"})
|
|
255
|
+
except ValidationError as e:
|
|
256
|
+
raise ValidationError({"primary_email": "Invalid e-mail address"}) from e
|
|
257
257
|
|
|
258
258
|
if primary_telephone := data.get("primary_telephone", None):
|
|
259
259
|
try:
|
|
@@ -265,8 +265,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
|
|
|
265
265
|
data["primary_telephone"] = formatted_number
|
|
266
266
|
else:
|
|
267
267
|
raise ValidationError({"primary_telephone": gettext("Invalid phone number format")})
|
|
268
|
-
except Exception:
|
|
269
|
-
raise ValidationError({"primary_telephone": gettext("Invalid phone number format")})
|
|
268
|
+
except Exception as e:
|
|
269
|
+
raise ValidationError({"primary_telephone": gettext("Invalid phone number format")}) from e
|
|
270
270
|
return super().validate(data)
|
|
271
271
|
|
|
272
272
|
def update(self, instance, validated_data):
|
|
@@ -131,8 +131,8 @@ class RelationshipModelSerializer(serializers.ModelSerializer):
|
|
|
131
131
|
data["from_entry_id"] = self.instance.from_entry.id
|
|
132
132
|
else:
|
|
133
133
|
data["from_entry_id"] = self.context["view"].kwargs["entry_id"]
|
|
134
|
-
except KeyError:
|
|
135
|
-
raise ValidationError(_("From entry has to be set."))
|
|
134
|
+
except KeyError as e:
|
|
135
|
+
raise ValidationError(_("From entry has to be set.")) from e
|
|
136
136
|
from_entry = data.get("from_entry", getattr(self.instance, "from_entry", None))
|
|
137
137
|
to_entry = data.get("to_entry", getattr(self.instance, "to_entry", None))
|
|
138
138
|
|
|
@@ -2,7 +2,17 @@ import inspect
|
|
|
2
2
|
import re
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
|
|
5
|
-
from django.db.models.signals import
|
|
5
|
+
from django.db.models.signals import (
|
|
6
|
+
ModelSignal,
|
|
7
|
+
post_delete,
|
|
8
|
+
post_init,
|
|
9
|
+
post_migrate,
|
|
10
|
+
post_save,
|
|
11
|
+
pre_delete,
|
|
12
|
+
pre_init,
|
|
13
|
+
pre_migrate,
|
|
14
|
+
pre_save,
|
|
15
|
+
)
|
|
6
16
|
|
|
7
17
|
|
|
8
18
|
class DisableSignals(object):
|
|
@@ -6,7 +6,7 @@ from wbcore.test.signals import custom_update_kwargs, get_custom_factory
|
|
|
6
6
|
from ..factories import (
|
|
7
7
|
BankFactory,
|
|
8
8
|
ParentRelationshipTypeFactory,
|
|
9
|
-
|
|
9
|
+
RandomClientFactory,
|
|
10
10
|
UserIsManagerEntryFactory,
|
|
11
11
|
)
|
|
12
12
|
from ..viewsets import (
|
|
@@ -27,7 +27,7 @@ from ..viewsets import (
|
|
|
27
27
|
|
|
28
28
|
@receiver(get_custom_factory, sender=PersonInChargeRepresentationViewSet)
|
|
29
29
|
def receive_factory_person_in_charge(sender, *args, **kwargs):
|
|
30
|
-
return
|
|
30
|
+
return RandomClientFactory
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
@receiver(get_custom_factory, sender=RelationshipTypeModelViewSet)
|