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.
Files changed (119) hide show
  1. wbcore/cache/decorators.py +3 -3
  2. wbcore/cache/registry.py +1 -1
  3. wbcore/configs/decorators.py +1 -1
  4. wbcore/configurations/configurations/apps.py +2 -2
  5. wbcore/configurations/configurations/authentication.py +1 -1
  6. wbcore/configurations/configurations/base.py +1 -1
  7. wbcore/configurations/configurations/cache.py +1 -1
  8. wbcore/configurations/configurations/maintenance.py +1 -1
  9. wbcore/configurations/configurations/media.py +1 -1
  10. wbcore/configurations/configurations/middleware.py +1 -1
  11. wbcore/configurations/configurations/rest_framework.py +1 -1
  12. wbcore/configurations/configurations/static.py +1 -1
  13. wbcore/configurations/configurations/wbcore.py +1 -1
  14. wbcore/content_type/serializers.py +1 -1
  15. wbcore/content_type/utils.py +3 -3
  16. wbcore/contrib/agenda/viewsets/calendar_items.py +7 -7
  17. wbcore/contrib/ai/llm/config.py +1 -1
  18. wbcore/contrib/authentication/admin.py +2 -2
  19. wbcore/contrib/authentication/models/users.py +3 -3
  20. wbcore/contrib/authentication/models/users_activities.py +1 -1
  21. wbcore/contrib/authentication/serializers/users.py +2 -2
  22. wbcore/contrib/authentication/tests/test_tokens.py +3 -3
  23. wbcore/contrib/authentication/tests/test_users.py +0 -1
  24. wbcore/contrib/authentication/viewsets/user_activities.py +2 -1
  25. wbcore/contrib/authentication/viewsets/users.py +6 -4
  26. wbcore/contrib/color/models.py +2 -1
  27. wbcore/contrib/currency/factories.py +1 -1
  28. wbcore/contrib/currency/import_export/backends/fixerio/currency_fx_rates.py +3 -1
  29. wbcore/contrib/currency/models.py +1 -1
  30. wbcore/contrib/dataloader/utils.py +2 -2
  31. wbcore/contrib/directory/factories/__init__.py +1 -1
  32. wbcore/contrib/directory/factories/entries.py +1 -1
  33. wbcore/contrib/directory/models/contacts.py +1 -1
  34. wbcore/contrib/directory/models/relationships.py +7 -6
  35. wbcore/contrib/directory/serializers/contacts.py +8 -8
  36. wbcore/contrib/directory/serializers/entries.py +4 -4
  37. wbcore/contrib/directory/serializers/relationships.py +2 -2
  38. wbcore/contrib/directory/tests/disable_signals.py +11 -1
  39. wbcore/contrib/directory/tests/signals.py +2 -2
  40. wbcore/contrib/directory/tests/test_models.py +56 -37
  41. wbcore/contrib/directory/tests/test_serializers.py +1 -1
  42. wbcore/contrib/directory/tests/test_viewsets.py +8 -8
  43. wbcore/contrib/directory/viewsets/contacts.py +6 -6
  44. wbcore/contrib/directory/viewsets/display/__init__.py +1 -1
  45. wbcore/contrib/directory/viewsets/display/relationships.py +22 -22
  46. wbcore/contrib/directory/viewsets/previews/entries.py +3 -3
  47. wbcore/contrib/directory/viewsets/titles/relationships.py +2 -3
  48. wbcore/contrib/example_app/models.py +4 -4
  49. wbcore/contrib/example_app/serializers/person_team.py +4 -4
  50. wbcore/contrib/example_app/tests/e2e/test_teams.py +1 -1
  51. wbcore/contrib/geography/tests/test_viewsets.py +1 -1
  52. wbcore/contrib/guardian/tests/test_model_mixins.py +3 -3
  53. wbcore/contrib/guardian/tests/test_tasks.py +9 -9
  54. wbcore/contrib/guardian/tests/test_viewsets.py +2 -2
  55. wbcore/contrib/icons/icons.py +4 -8
  56. wbcore/contrib/io/import_export/backends/stream.py +2 -2
  57. wbcore/contrib/io/imports.py +1 -1
  58. wbcore/contrib/io/models.py +17 -14
  59. wbcore/contrib/io/serializers.py +2 -2
  60. wbcore/contrib/io/tests/test_backends.py +1 -1
  61. wbcore/contrib/io/tests/test_imports.py +1 -1
  62. wbcore/contrib/io/viewset_mixins.py +4 -4
  63. wbcore/contrib/notifications/dispatch.py +3 -1
  64. wbcore/contrib/pandas/filterset.py +8 -7
  65. wbcore/contrib/pandas/views.py +7 -5
  66. wbcore/contrib/tags/models/tags.py +4 -1
  67. wbcore/contrib/workflow/factories/display.py +2 -2
  68. wbcore/contrib/workflow/models/data.py +7 -4
  69. wbcore/contrib/workflow/models/process.py +2 -2
  70. wbcore/contrib/workflow/serializers/data.py +8 -8
  71. wbcore/contrib/workflow/tests/test_models/test_condition.py +1 -1
  72. wbcore/contrib/workflow/workflows/assignees.py +4 -4
  73. wbcore/enums.py +2 -1
  74. wbcore/filters/fields/datetime.py +1 -1
  75. wbcore/filters/fields/models.py +2 -2
  76. wbcore/filters/filterset.py +4 -4
  77. wbcore/forms.py +4 -4
  78. wbcore/fsm/markdown_extensions.py +1 -1
  79. wbcore/fsm/mixins.py +3 -3
  80. wbcore/markdown/models.py +8 -5
  81. wbcore/metadata/configs/buttons/bases.py +6 -6
  82. wbcore/metadata/configs/buttons/buttons.py +2 -1
  83. wbcore/metadata/configs/buttons/view_config.py +5 -3
  84. wbcore/metadata/configs/display/display.py +2 -2
  85. wbcore/metadata/configs/display/formatting.py +6 -7
  86. wbcore/metadata/configs/display/list_display.py +2 -4
  87. wbcore/metadata/configs/display/models.py +6 -0
  88. wbcore/models/fields.py +2 -2
  89. wbcore/permissions/utils.py +2 -2
  90. wbcore/reversion/viewsets/titles.py +4 -3
  91. wbcore/serializers/fields/datetime.py +3 -3
  92. wbcore/serializers/fields/fields.py +1 -1
  93. wbcore/serializers/fields/fsm.py +1 -1
  94. wbcore/serializers/fields/list.py +1 -1
  95. wbcore/serializers/fields/mixins.py +5 -5
  96. wbcore/serializers/fields/related.py +3 -5
  97. wbcore/serializers/fields/text.py +1 -1
  98. wbcore/serializers/serializers.py +2 -2
  99. wbcore/tasks.py +2 -2
  100. wbcore/test/e2e_helpers_methods/e2e_checks.py +10 -4
  101. wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +4 -2
  102. wbcore/test/tests.py +6 -9
  103. wbcore/test/utils.py +2 -3
  104. wbcore/tests/e2e/test_e2e.py +2 -2
  105. wbcore/tests/test_cache/test_decorators.py +3 -3
  106. wbcore/tests/test_configs.py +1 -1
  107. wbcore/tests/test_fields/test_number_fields.py +1 -1
  108. wbcore/tests/test_filters/test_mixins.py +3 -3
  109. wbcore/tests/test_models/test_mixins.py +1 -1
  110. wbcore/tests/test_utils/test_date.py +1 -1
  111. wbcore/tests/test_utils/test_date_builder.py +25 -1
  112. wbcore/utils/date.py +4 -1
  113. wbcore/utils/figures.py +2 -2
  114. wbcore/utils/models.py +3 -2
  115. wbcore/utils/string_loader.py +1 -1
  116. wbcore/viewsets/mixins.py +6 -4
  117. {wbcore-1.55.9.dist-info → wbcore-1.55.10rc0.dist-info}/METADATA +1 -1
  118. {wbcore-1.55.9.dist-info → wbcore-1.55.10rc0.dist-info}/RECORD +119 -119
  119. {wbcore-1.55.9.dist-info → wbcore-1.55.10rc0.dist-info}/WHEEL +0 -0
@@ -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
- setattr(pandas_view_class, "CACHE_ENABLED", not settings.DEBUG)
19
- setattr(pandas_view_class, "CACHE_TIMEOUT", timeout)
20
- setattr(pandas_view_class, "CACHE_KEY_PREFIX", key_prefix)
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
- setattr(request, "parser_context", {"request": request, "view": view, "kwargs": kwargs})
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())
@@ -7,5 +7,5 @@ def register_config(func: Callable) -> Callable:
7
7
  of all configs. This function should only be used in a module called configs, otherwise the auto-discover
8
8
  functionality won't work.
9
9
  """
10
- setattr(func, "_is_config", True)
10
+ func._is_config = True
11
11
  return func
@@ -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
@@ -8,7 +8,7 @@ class Cache:
8
8
  ) # The default (10 days) timeout in seconds
9
9
 
10
10
  @property
11
- def CACHES(self):
11
+ def CACHES(self): # noqa
12
12
  if self.CACHE_REDIS_URL:
13
13
  return {
14
14
  "default": {
@@ -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:
@@ -7,7 +7,7 @@ class Media:
7
7
 
8
8
  class LocalMedia(Media):
9
9
  @property
10
- def MEDIA_ROOT(self):
10
+ def MEDIA_ROOT(self): # noqa
11
11
  return self.BASE_DIR.joinpath("mediafiles")
12
12
 
13
13
 
@@ -14,7 +14,7 @@ class Middleware:
14
14
 
15
15
  class DevMiddleware:
16
16
  @property
17
- def MIDDLEWARE(self):
17
+ def MIDDLEWARE(self): # noqa
18
18
  middleware = []
19
19
  middleware_list = Middleware.MIDDLEWARE.copy()
20
20
  if self.DEBUG:
@@ -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,
@@ -13,7 +13,7 @@ class LocalStaticfiles(Staticfiles):
13
13
  }
14
14
 
15
15
  @property
16
- def STATIC_ROOT(self):
16
+ def STATIC_ROOT(self): # noqa
17
17
  return self.BASE_DIR.joinpath("staticfiles")
18
18
 
19
19
 
@@ -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
 
@@ -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
- try:
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__(metacls, cls, bases, classdict, **kwds):
42
- _class = super().__new__(metacls, cls, bases, classdict, **kwds)
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
- setattr(_class, "DEPENDANT_IDENTIFIERS", dependant_identifiers)
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 tmp, union_qs in draggable_calendar_item_ids.send(CalendarItem, request=self.request):
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
- iCal = Calendar()
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
- iCal.events.add(event)
165
- data = StringIO(str(iCal))
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
@@ -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
- adminForm = admin.helpers.AdminForm(form, fieldsets, {})
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": 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(self):
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(self):
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(self):
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(self):
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(user):
20
+ def request_with_user(self: User):
21
21
  request = APIRequestFactory().get("/")
22
- request.user = 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") for start, end in zip(dff.date, dff.latest_refresh)
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
- try:
70
+ with suppress(User.DoesNotExist):
70
71
  user = User.objects.get(email=request.data["email"])
71
- user.reset_password(request)
72
- except Exception:
73
- pass
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",
@@ -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, key=lambda subject: sum((s - q) ** 2 for s, q in zip(_hex2rgb(subject), color_rgb))
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(f'{self.ENDPOINT}/{_date.strftime("%Y-%m-%d")}?', params=params, headers=self.HEADERS)
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))
@@ -18,7 +18,7 @@ from .entries import (
18
18
  EntryFactory,
19
19
  PersonFactory,
20
20
  PersonWithEmployerFactory,
21
- Random_ClientFactory,
21
+ RandomClientFactory,
22
22
  SpecializationFactory,
23
23
  UnemployedPersonFactory,
24
24
  PersonSignatureFactory,
@@ -158,7 +158,7 @@ class ClientFactory(PersonFactory):
158
158
  self.clients.add(person)
159
159
 
160
160
 
161
- class Random_ClientFactory(ClientFactory):
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(instance):
311
- return not instance.primary
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(instance):
335
- return not instance.primary and (
336
- ClientManagerRelationship.objects.exclude(id=instance.id)
337
- .filter(status=ClientManagerRelationship.Status.APPROVED, client=instance.client, primary=True)
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
- Random_ClientFactory,
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 Random_ClientFactory
30
+ return RandomClientFactory
31
31
 
32
32
 
33
33
  @receiver(get_custom_factory, sender=RelationshipTypeModelViewSet)