clinicedc 2.0.7__py3-none-any.whl → 2.0.8__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.

Potentially problematic release.


This version of clinicedc might be problematic. Click here for more details.

Files changed (140) hide show
  1. {clinicedc-2.0.7.dist-info → clinicedc-2.0.8.dist-info}/METADATA +1 -1
  2. {clinicedc-2.0.7.dist-info → clinicedc-2.0.8.dist-info}/RECORD +134 -137
  3. {clinicedc-2.0.7.dist-info → clinicedc-2.0.8.dist-info}/WHEEL +1 -1
  4. edc_action_item/auths.py +37 -32
  5. edc_action_item/models/action_model_mixin.py +1 -2
  6. edc_action_item/models/signals.py +22 -23
  7. edc_action_item/site_action_items.py +5 -9
  8. edc_action_item/utils.py +3 -3
  9. edc_adverse_event/auths.py +55 -51
  10. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +2 -4
  11. edc_appointment/auths.py +14 -10
  12. edc_appointment/creators/appointment_creator.py +1 -1
  13. edc_appointment/creators/appointments_creator.py +1 -1
  14. edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -3
  15. edc_appointment/model_mixins/appointment_model_mixin.py +31 -28
  16. edc_appointment/models/appointment.py +1 -1
  17. edc_appointment/utils.py +19 -24
  18. edc_auth/auth_objects/__init__.py +2 -20
  19. edc_auth/auth_objects/default_groups.py +13 -11
  20. edc_auth/auth_objects/default_roles.py +26 -24
  21. edc_auth/auth_updater/auth_updater.py +13 -2
  22. edc_auth/auth_updater/group_updater.py +12 -10
  23. edc_auth/auth_updater/role_updater.py +2 -2
  24. edc_auth/constants.py +10 -0
  25. edc_auth/import_users.py +3 -3
  26. edc_auth/models/user_profile.py +14 -11
  27. edc_auth/site_auths.py +80 -67
  28. edc_consent/auths.py +18 -12
  29. edc_constants/constants.py +1 -0
  30. edc_crf/auths.py +5 -0
  31. edc_dashboard/auths.py +10 -6
  32. edc_dashboard/url_config.py +92 -83
  33. edc_dashboard/url_names.py +4 -4
  34. edc_dashboard/view_mixins/url_request_context_mixin.py +6 -5
  35. edc_data_manager/admin/data_query_admin.py +12 -11
  36. edc_data_manager/auths.py +37 -34
  37. edc_data_manager/rule/query_rule_wrapper.py +7 -7
  38. edc_export/archive_exporter.py +3 -2
  39. edc_export/auths.py +32 -28
  40. edc_export/model_exporter/model_exporter.py +4 -1
  41. edc_facility/auths.py +8 -3
  42. edc_facility/facility.py +8 -9
  43. edc_form_label/custom_label_condition.py +11 -8
  44. edc_form_label/form_label.py +1 -1
  45. edc_form_runners/auths.py +11 -6
  46. edc_form_validators/applicable_field_validator.py +7 -6
  47. edc_form_validators/base_form_validator.py +8 -9
  48. edc_form_validators/other_specify_field_validator.py +2 -8
  49. edc_form_validators/required_field_validator.py +19 -16
  50. edc_identifier/research_identifier.py +11 -10
  51. edc_identifier/simple_identifier.py +8 -2
  52. edc_lab/auths.py +26 -23
  53. edc_lab/lab/aliquot_creator.py +5 -8
  54. edc_lab/lab/primary_aliquot.py +14 -5
  55. edc_lab/model_mixins/requisition/requisition_model_mixin.py +6 -8
  56. edc_lab/models/aliquot.py +2 -2
  57. edc_lab/models/manifest/manifest.py +2 -2
  58. edc_lab/models/manifest/manifest_item.py +1 -1
  59. edc_lab_dashboard/auths.py +16 -11
  60. edc_lab_results/calculate_missing.py +8 -8
  61. edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +2 -2
  62. edc_lab_results/get_summary.py +26 -25
  63. edc_lab_results/model_mixins/blood_result_model_mixin.py +2 -0
  64. edc_label/auths.py +6 -1
  65. edc_label/label_template.py +8 -8
  66. edc_list_data/load_model_data.py +3 -3
  67. edc_list_data/post_migrate_signals.py +1 -1
  68. edc_list_data/preload_data.py +2 -2
  69. edc_list_data/row.py +1 -1
  70. edc_list_data/site_list_data.py +6 -5
  71. edc_locator/auths.py +18 -13
  72. edc_metadata/auths.py +11 -7
  73. edc_metadata/metadata/metadata.py +1 -1
  74. edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
  75. edc_metadata/metadata_rules/metadata_rule_evaluator.py +5 -3
  76. edc_metadata/metadata_rules/rule.py +2 -3
  77. edc_metadata/metadata_rules/rule_evaluator.py +1 -1
  78. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +7 -4
  79. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +5 -2
  80. edc_metadata/models/signals.py +10 -11
  81. edc_navbar/auths.py +18 -13
  82. edc_notification/auths.py +9 -4
  83. edc_notification/notification/graded_event_notification.py +2 -2
  84. edc_notification/notification/model_notification.py +3 -30
  85. edc_notification/notification/new_model_notification.py +1 -1
  86. edc_notification/notification/notification.py +1 -1
  87. edc_notification/notification/updated_model_notification.py +2 -2
  88. edc_offstudy/auths.py +12 -7
  89. edc_pdutils/df_exporters/csv_model_exporter.py +5 -2
  90. edc_pharmacy/auths.py +19 -15
  91. edc_pharmacy/models/medication/formulation.py +5 -7
  92. edc_pharmacy/prescribe/create_prescription.py +3 -3
  93. edc_pharmacy/utils/confirm_stock.py +1 -1
  94. edc_pharmacy/utils/confirm_stock_at_site.py +1 -1
  95. edc_pharmacy/views/confirmation_at_site_view.py +6 -9
  96. edc_prn/admin_site.py +5 -0
  97. edc_prn/prn.py +10 -11
  98. edc_prn/urls.py +11 -0
  99. edc_protocol_incident/action_items.py +4 -4
  100. edc_protocol_incident/auths.py +27 -20
  101. edc_pylabels/auths.py +6 -1
  102. edc_qareports/auths.py +11 -7
  103. edc_randomization/admin.py +30 -24
  104. edc_randomization/auths.py +12 -7
  105. edc_randomization/randomizer.py +22 -20
  106. edc_randomization/utils.py +17 -16
  107. edc_refusal/auths.py +7 -2
  108. edc_refusal/model_mixins.py +1 -1
  109. edc_registration/auths.py +28 -23
  110. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +13 -4
  111. edc_registration/models/registered_subject.py +1 -1
  112. edc_reportable/utils/get_reference_range_collection.py +2 -3
  113. edc_reportable/utils/load_data.py +1 -1
  114. edc_review_dashboard/auths.py +23 -18
  115. edc_screening/age_evaluator.py +3 -3
  116. edc_screening/auths.py +35 -30
  117. edc_screening/eligibility.py +1 -1
  118. edc_screening/gender_evaluator.py +1 -1
  119. edc_screening/model_mixins/eligibility_model_mixin.py +0 -2
  120. edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
  121. edc_screening/screening_eligibility.py +2 -4
  122. edc_screening/utils.py +9 -9
  123. edc_search/generate_slug.py +26 -0
  124. edc_search/model_mixins.py +10 -21
  125. edc_sites/auths.py +8 -3
  126. edc_subject_dashboard/auths.py +27 -22
  127. edc_timepoint/apps.py +0 -21
  128. edc_unblinding/auths.py +9 -4
  129. edc_utils/__init__.py +3 -1
  130. edc_utils/show_urls.py +29 -2
  131. edc_visit_schedule/auths.py +6 -1
  132. edc_visit_schedule/site_visit_schedules.py +2 -2
  133. edc_visit_tracking/models/signals.py +2 -2
  134. edc_form_label/models.py +0 -0
  135. edc_search/constants.py +0 -1
  136. edc_search/models.py +0 -0
  137. edc_search/search_slug.py +0 -51
  138. edc_search/updater.py +0 -30
  139. edc_search/wsgi.py +0 -7
  140. {clinicedc-2.0.7.dist-info → clinicedc-2.0.8.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import warnings
4
5
  from datetime import datetime
5
6
  from pathlib import Path
@@ -8,6 +9,7 @@ from typing import TYPE_CHECKING, Any
8
9
  from django.apps import apps as django_apps
9
10
  from django.conf import settings
10
11
  from django.core.exceptions import ObjectDoesNotExist, ValidationError
12
+ from django.db.models import Q
11
13
 
12
14
  from edc_registration.utils import get_registered_subject_model_cls
13
15
 
@@ -25,15 +27,15 @@ if TYPE_CHECKING:
25
27
  from edc_registration.models import RegisteredSubject
26
28
 
27
29
 
28
- class InvalidAssignmentDescriptionMap(Exception):
30
+ class InvalidAssignmentDescriptionMap(Exception): # noqa: N818
29
31
  pass
30
32
 
31
33
 
32
- class RandomizationListFileNotFound(Exception):
34
+ class RandomizationListFileNotFound(Exception): # noqa: N818
33
35
  pass
34
36
 
35
37
 
36
- class RandomizationListNotLoaded(Exception):
38
+ class RandomizationListNotLoaded(Exception): # noqa: N818
37
39
  pass
38
40
 
39
41
 
@@ -41,7 +43,7 @@ class RandomizationError(Exception):
41
43
  pass
42
44
 
43
45
 
44
- class AlreadyRandomized(ValidationError):
46
+ class AlreadyRandomized(ValidationError): # noqa: N818
45
47
  pass
46
48
 
47
49
 
@@ -113,10 +115,10 @@ class Randomizer:
113
115
  subject_identifier: str | None = None,
114
116
  identifier_attr: str | None = None,
115
117
  identifier_object_name: str | None = None,
116
- report_datetime: datetime = None,
117
- site: Any = None,
118
- user: str = None,
119
- **kwargs,
118
+ report_datetime: datetime | None = None,
119
+ site: Any | None = None,
120
+ user: str | None = None,
121
+ **kwargs, # noqa: ARG002
120
122
  ):
121
123
  self._model_obj = None
122
124
  self._registration_obj = None
@@ -208,8 +210,10 @@ class Randomizer:
208
210
  @property
209
211
  def sid(self):
210
212
  """Returns the SID."""
211
- if self.model_obj.sid is None:
212
- raise RandomizationError(f"SID cannot be None. See {self.model_obj}.")
213
+ if not self.model_obj.sid:
214
+ raise RandomizationError(
215
+ f"SID cannot be None. See {self.model_obj}. Got {self.model_obj.sid}"
216
+ )
213
217
  return self.model_obj.sid
214
218
 
215
219
  @property
@@ -233,7 +237,7 @@ class Randomizer:
233
237
  if not self._model_obj:
234
238
  try:
235
239
  obj = self.model_cls().objects.get(**self.identifier_opts)
236
- except ObjectDoesNotExist:
240
+ except ObjectDoesNotExist as e:
237
241
  opts = dict(site_name=self.site.name, **self.extra_model_obj_options)
238
242
  self._model_obj = (
239
243
  self.model_cls()
@@ -245,7 +249,7 @@ class Randomizer:
245
249
  fld_str = ", ".join([f"{k}=`{v}`" for k, v in opts.items()])
246
250
  raise AllocationError(
247
251
  f"Randomization failed. No additional SIDs available for {fld_str}."
248
- )
252
+ ) from e
249
253
  else:
250
254
  raise AlreadyRandomized(
251
255
  f"{self.identifier_object_name.title()} already randomized. "
@@ -282,7 +286,7 @@ class Randomizer:
282
286
  Called by `registration_obj`.
283
287
  """
284
288
  return self.get_registration_model_cls().objects.get(
285
- sid__isnull=True, **self.identifier_opts
289
+ (Q(sid__isnull=True) | Q(sid="")), **self.identifier_opts
286
290
  )
287
291
 
288
292
  @property
@@ -298,14 +302,14 @@ class Randomizer:
298
302
  if not self._registration_obj:
299
303
  try:
300
304
  self._registration_obj = self.get_unallocated_registration_obj()
301
- except ObjectDoesNotExist:
305
+ except ObjectDoesNotExist as e:
302
306
  try:
303
307
  obj = self.get_registration_model_cls().objects.get(**self.identifier_opts)
304
- except ObjectDoesNotExist:
308
+ except ObjectDoesNotExist as e:
305
309
  raise RandomizationError(
306
310
  f"{self.identifier_object_name.title()} does not exist. "
307
311
  f"Got {getattr(self, self.identifier_attr)}"
308
- )
312
+ ) from e
309
313
  else:
310
314
  raise AlreadyRandomized(
311
315
  f"{self.identifier_object_name.title()} already randomized. "
@@ -313,7 +317,7 @@ class Randomizer:
313
317
  f"Got {getattr(obj, self.identifier_attr)} "
314
318
  f"SID={obj.sid}",
315
319
  code=self.get_registration_model_cls()._meta.label_lower,
316
- )
320
+ ) from e
317
321
  return self._registration_obj
318
322
 
319
323
  @property
@@ -347,7 +351,7 @@ class Randomizer:
347
351
  "Randomization list file not found. "
348
352
  f"Got `{cls.get_randomizationlist_path()}`. See Randomizer {cls.name}."
349
353
  )
350
- try:
354
+ with contextlib.suppress(RandomizationListAlreadyImported):
351
355
  result = cls.importer_cls(
352
356
  assignment_map=cls.assignment_map,
353
357
  randomizationlist_path=cls.get_randomizationlist_path(),
@@ -356,8 +360,6 @@ class Randomizer:
356
360
  extra_csv_fieldnames=cls.extra_csv_fieldnames,
357
361
  **kwargs,
358
362
  ).import_list(**kwargs)
359
- except RandomizationListAlreadyImported:
360
- pass
361
363
  return result
362
364
 
363
365
  @classmethod
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import csv
4
- import os
4
+ import sys
5
+ from pathlib import Path
5
6
  from typing import Any
6
7
 
7
8
  from django.conf import settings
@@ -20,13 +21,13 @@ class RandomizationListExporterError(Exception):
20
21
  pass
21
22
 
22
23
 
23
- class SubjectNotRandomization(Exception):
24
+ class SubjectNotRandomization(Exception): # noqa: N818
24
25
  pass
25
26
 
26
27
 
27
28
  def get_assignment_for_subject(
28
29
  subject_identifier: str,
29
- randomizer_name: str = None,
30
+ randomizer_name: str,
30
31
  identifier_fld: str | None = None,
31
32
  ) -> str:
32
33
  """Returns the assignment for a randomized subject.
@@ -42,7 +43,7 @@ def get_assignment_for_subject(
42
43
 
43
44
  def get_assignment_description_for_subject(
44
45
  subject_identifier: str,
45
- randomizer_name: str = None,
46
+ randomizer_name: str,
46
47
  identifier_fld: str | None = None,
47
48
  ) -> str:
48
49
  """Returns the assignment description for a randomized subject.
@@ -60,7 +61,7 @@ def get_assignment_description_for_subject(
60
61
 
61
62
  def get_object_for_subject(
62
63
  subject_identifier: str,
63
- randomizer_name: str = None,
64
+ randomizer_name: str,
64
65
  identifier_fld: str | None = None,
65
66
  label: str | None = None,
66
67
  ) -> Any:
@@ -81,11 +82,11 @@ def get_object_for_subject(
81
82
  }
82
83
  try:
83
84
  obj = randomizer_cls.model_cls().objects.get(**opts)
84
- except ObjectDoesNotExist:
85
+ except ObjectDoesNotExist as e:
85
86
  raise SubjectNotRandomization(
86
87
  f"{label.title()} not randomized. See Randomizer `{randomizer_name}`. "
87
88
  f"Got {identifier_fld}=`{subject_identifier}`."
88
- )
89
+ ) from e
89
90
  return obj
90
91
 
91
92
 
@@ -118,7 +119,7 @@ def generate_fake_randomization_list(
118
119
 
119
120
  # get site ID and write the file
120
121
  site_id = sites.get_by_attr("name", site_name)
121
- with open(filename, "a+", newline="") as f:
122
+ with Path(filename).open("a+", newline="") as f:
122
123
  writer = csv.DictWriter(f, fieldnames=["sid", "assignment", "site_name", "country"])
123
124
  if write_header:
124
125
  writer.writeheader()
@@ -133,7 +134,7 @@ def generate_fake_randomization_list(
133
134
  )
134
135
  )
135
136
 
136
- print(f"(*) Added {slots} slots for {site_name}.")
137
+ sys.stdout.write(f"(*) Added {slots} slots for {site_name}.\n")
137
138
 
138
139
 
139
140
  def export_randomization_list(
@@ -143,20 +144,20 @@ def export_randomization_list(
143
144
 
144
145
  try:
145
146
  user = get_user_model().objects.get(username=username)
146
- except ObjectDoesNotExist:
147
- raise RandomizationListExporterError(f"User `{username}` does not exist")
147
+ except ObjectDoesNotExist as e:
148
+ raise RandomizationListExporterError(f"User `{username}` does not exist") from e
148
149
  if not user.has_perm(randomizer_cls.model_cls()._meta.label_lower.replace(".", ".view_")):
149
150
  raise RandomizationListExporterError(
150
151
  f"User `{username}` does not have "
151
152
  f"permission to view '{randomizer_cls.model_cls()._meta.label_lower}'"
152
153
  )
153
- path = path or settings.EXPORT_FOLDER
154
+ path = Path(path or settings.EXPORT_FOLDER)
154
155
  timestamp = timezone.now().strftime("%Y%m%d%H%M")
155
- filename = os.path.expanduser(
156
+ filename = Path(
156
157
  f"~/{settings.APP_NAME}_{randomizer_cls.name}_"
157
158
  f"randomizationlist_exported_{timestamp}.csv"
158
- )
159
- filename = os.path.join(path, filename)
159
+ ).expanduser()
160
+ filename = path / filename
160
161
 
161
162
  df = (
162
163
  read_frame(randomizer_cls.model_cls().objects.all(), verbose=False)
@@ -172,5 +173,5 @@ def export_randomization_list(
172
173
  sep="|",
173
174
  )
174
175
  df.to_csv(**opts)
175
- print(filename)
176
+ sys.stdout.write(f"{filename!s}\n")
176
177
  return filename
edc_refusal/auths.py CHANGED
@@ -3,5 +3,10 @@ from edc_screening.auth_objects import SCREENING, SCREENING_SUPER
3
3
 
4
4
  from .auth_objects import codenames
5
5
 
6
- site_auths.update_group(*codenames, name=SCREENING, no_delete=True)
7
- site_auths.update_group(*codenames, name=SCREENING_SUPER)
6
+
7
+ def update_site_auths() -> None:
8
+ site_auths.update_group(*codenames, name=SCREENING, no_delete=True)
9
+ site_auths.update_group(*codenames, name=SCREENING_SUPER)
10
+
11
+
12
+ update_site_auths()
@@ -33,7 +33,7 @@ class SubjectRefusalModelMixin(models.Model):
33
33
 
34
34
  @staticmethod
35
35
  def get_search_slug_fields():
36
- return ["screening_identifier"]
36
+ return ("screening_identifier",)
37
37
 
38
38
  class Meta:
39
39
  abstract = True
edc_registration/auths.py CHANGED
@@ -4,27 +4,32 @@ from edc_auth.constants import PII, PII_VIEW
4
4
  from edc_auth.site_auths import site_auths
5
5
  from edc_export.constants import EXPORT
6
6
 
7
- if django_apps.is_installed("edc_export"):
8
- site_auths.update_group("edc_registration.export_registeredsubject", name=EXPORT)
9
- site_auths.update_group(
10
- "edc_registration.display_dob",
11
- "edc_registration.display_firstname",
12
- "edc_registration.display_identity",
13
- "edc_registration.display_initials",
14
- "edc_registration.display_lastname",
15
- "edc_registration.view_historicalregisteredsubject",
16
- "edc_registration.view_registeredsubject",
17
- name=PII,
18
- )
19
7
 
20
- site_auths.update_group(
21
- "edc_registration.display_dob",
22
- "edc_registration.display_firstname",
23
- "edc_registration.display_identity",
24
- "edc_registration.display_initials",
25
- "edc_registration.display_lastname",
26
- "edc_registration.view_historicalregisteredsubject",
27
- "edc_registration.view_registeredsubject",
28
- name=PII_VIEW,
29
- )
30
- site_auths.add_pii_model("edc_registration.registeredsubject")
8
+ def update_site_auths() -> None:
9
+ if django_apps.is_installed("edc_export"):
10
+ site_auths.update_group("edc_registration.export_registeredsubject", name=EXPORT)
11
+ site_auths.update_group(
12
+ "edc_registration.display_dob",
13
+ "edc_registration.display_firstname",
14
+ "edc_registration.display_identity",
15
+ "edc_registration.display_initials",
16
+ "edc_registration.display_lastname",
17
+ "edc_registration.view_historicalregisteredsubject",
18
+ "edc_registration.view_registeredsubject",
19
+ name=PII,
20
+ )
21
+
22
+ site_auths.update_group(
23
+ "edc_registration.display_dob",
24
+ "edc_registration.display_firstname",
25
+ "edc_registration.display_identity",
26
+ "edc_registration.display_initials",
27
+ "edc_registration.display_lastname",
28
+ "edc_registration.view_historicalregisteredsubject",
29
+ "edc_registration.view_registeredsubject",
30
+ name=PII_VIEW,
31
+ )
32
+ site_auths.add_pii_model("edc_registration.registeredsubject")
33
+
34
+
35
+ update_site_auths()
@@ -5,7 +5,9 @@ from typing import TYPE_CHECKING
5
5
 
6
6
  from django.core.exceptions import ObjectDoesNotExist
7
7
  from django.db import models
8
+ from django.db.models import CharField, TextField
8
9
 
10
+ from edc_constants.constants import NULL_STRING
9
11
  from edc_model import DEFAULT_BASE_FIELDS
10
12
 
11
13
  from ..utils import get_registered_subject_model_cls
@@ -99,13 +101,20 @@ class UpdatesOrCreatesRegistrationModelMixin(models.Model):
99
101
  """
100
102
  registration_options = {}
101
103
  rs = self.registration_model()
102
- for k, v in self.__dict__.items():
103
- if k not in DEFAULT_BASE_FIELDS + ["_state"]:
104
+ for fname, value in self.__dict__.items():
105
+ if fname not in (*DEFAULT_BASE_FIELDS, "_state"):
104
106
  try:
105
- getattr(rs, k)
106
- registration_options.update({k: v})
107
+ getattr(rs, fname)
107
108
  except AttributeError:
108
109
  pass
110
+ else:
111
+ value = ( # noqa: PLW2901
112
+ NULL_STRING
113
+ if value is None
114
+ and isinstance(rs._meta.get_field(fname), (CharField, TextField))
115
+ else value
116
+ )
117
+ registration_options.update({fname: value})
109
118
  registration_identifier = registration_options.get("registration_identifier")
110
119
  if registration_identifier:
111
120
  registration_options["registration_identifier"] = self.to_string(
@@ -143,7 +143,7 @@ class RegisteredSubject(UniqueSubjectIdentifierModelMixin, SiteModelMixin, BaseU
143
143
 
144
144
  def save(self, *args, **kwargs):
145
145
  if self.identity:
146
- self.additional_key = None
146
+ self.additional_key = ""
147
147
  self.set_uuid_as_subject_identifier_if_none()
148
148
  self.raise_on_duplicate("subject_identifier")
149
149
  self.raise_on_duplicate("identity")
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  from typing import TYPE_CHECKING
4
5
 
5
6
  from django.core.exceptions import ObjectDoesNotExist
@@ -21,10 +22,8 @@ def get_reference_range_collection(obj) -> ReferenceRangeCollection:
21
22
  name = obj.requisition.panel_object.reference_range_collection_name
22
23
  except AttributeError:
23
24
  name = obj.panel_object.reference_range_collection_name
24
- try:
25
+ with contextlib.suppress(ObjectDoesNotExist):
25
26
  reference_range_collection = reference_range_colllection_model_cls().objects.get(
26
27
  name=name
27
28
  )
28
- except ObjectDoesNotExist:
29
- pass
30
29
  return reference_range_collection
@@ -60,7 +60,7 @@ def load_reference_ranges(
60
60
  collection_name: str,
61
61
  normal_data: dict[str, list[Formula]],
62
62
  grading_data: dict[str, list[Formula]],
63
- reportable_grades: list[int],
63
+ reportable_grades: list[int] | None = None,
64
64
  reportable_grades_exceptions: dict[str, list[int]] | None = None,
65
65
  keep_existing: bool | None = None,
66
66
  create_missing_normal: bool | None = None,
@@ -10,23 +10,28 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
10
10
 
11
11
  REVIEW = "REVIEW"
12
12
 
13
- site_auths.add_post_update_func(
14
- "edc_review_dashboard", remove_default_model_permissions_from_edc_permissions
15
- )
16
13
 
17
- site_auths.add_custom_permissions_tuples(
18
- model="edc_review_dashboard.edcpermissions",
19
- codename_tuples=[
20
- (
21
- "edc_review_dashboard.view_subject_review_listboard",
22
- "Can access subject review listboard",
23
- )
24
- ],
25
- )
26
- site_auths.add_group("edc_review_dashboard.view_subject_review_listboard", name=REVIEW)
14
+ def update_site_auths():
15
+ site_auths.add_post_update_func(
16
+ "edc_review_dashboard", remove_default_model_permissions_from_edc_permissions
17
+ )
18
+
19
+ site_auths.add_custom_permissions_tuples(
20
+ model="edc_review_dashboard.edcpermissions",
21
+ codename_tuples=[
22
+ (
23
+ "edc_review_dashboard.view_subject_review_listboard",
24
+ "Can access subject review listboard",
25
+ )
26
+ ],
27
+ )
28
+ site_auths.add_group("edc_review_dashboard.view_subject_review_listboard", name=REVIEW)
29
+
30
+ site_auths.update_role(REVIEW, name=AUDITOR_ROLE)
31
+ site_auths.update_role(REVIEW, name=TMG_ROLE)
32
+ site_auths.update_role(REVIEW, name=CLINICIAN_ROLE)
33
+ site_auths.update_role(REVIEW, name=CLINICIAN_SUPER_ROLE)
34
+ site_auths.update_role(REVIEW, name=NURSE_ROLE)
35
+
27
36
 
28
- site_auths.update_role(REVIEW, name=AUDITOR_ROLE)
29
- site_auths.update_role(REVIEW, name=TMG_ROLE)
30
- site_auths.update_role(REVIEW, name=CLINICIAN_ROLE)
31
- site_auths.update_role(REVIEW, name=CLINICIAN_SUPER_ROLE)
32
- site_auths.update_role(REVIEW, name=NURSE_ROLE)
37
+ update_site_auths()
@@ -8,11 +8,11 @@ from edc_reportable.exceptions import ValueBoundryError
8
8
 
9
9
  class AgeEvaluator(ReportableAgeEvaluator):
10
10
  def __init__(self, **kwargs) -> None:
11
- self.reasons_ineligible: str | None = None
11
+ self.reasons_ineligible: str = ""
12
12
  super().__init__(**kwargs)
13
13
 
14
14
  def eligible(self, age: int | None = None) -> bool:
15
- self.reasons_ineligible = None
15
+ self.reasons_ineligible = ""
16
16
  eligible = False
17
17
  if age:
18
18
  try:
@@ -26,7 +26,7 @@ class AgeEvaluator(ReportableAgeEvaluator):
26
26
  return eligible
27
27
 
28
28
  def in_bounds_or_raise(self, age: int = None, **kwargs):
29
- self.reasons_ineligible = None
29
+ self.reasons_ineligible = ""
30
30
  dob = localtime(timezone.now() - relativedelta(years=age)).date()
31
31
  age_units = "years"
32
32
  report_datetime = localtime(timezone.now())
edc_screening/auths.py CHANGED
@@ -9,38 +9,43 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
9
9
 
10
10
  from .auth_objects import SCREENING, SCREENING_ROLE, SCREENING_SUPER, SCREENING_VIEW
11
11
 
12
- site_auths.add_post_update_func(
13
- "edc_screening", remove_default_model_permissions_from_edc_permissions
14
- )
15
12
 
16
- site_auths.add_custom_permissions_tuples(
17
- model="edc_screening.edcpermissions",
18
- codename_tuples=(
19
- ("edc_screening.view_screening_listboard", "Can access Screening listboard"),
20
- ("edc_screening.nav_screening_section", "Can access screening section"),
21
- ),
22
- )
13
+ def update_site_auths() -> None:
14
+ site_auths.add_post_update_func(
15
+ "edc_screening", remove_default_model_permissions_from_edc_permissions
16
+ )
23
17
 
24
- site_auths.add_group(
25
- "edc_screening.view_screening_listboard",
26
- "edc_screening.nav_screening_section",
27
- name=SCREENING,
28
- )
18
+ site_auths.add_custom_permissions_tuples(
19
+ model="edc_screening.edcpermissions",
20
+ codename_tuples=(
21
+ ("edc_screening.view_screening_listboard", "Can access Screening listboard"),
22
+ ("edc_screening.nav_screening_section", "Can access screening section"),
23
+ ),
24
+ )
29
25
 
30
- site_auths.add_group(
31
- "edc_screening.view_screening_listboard",
32
- "edc_screening.nav_screening_section",
33
- name=SCREENING_SUPER,
34
- )
26
+ site_auths.add_group(
27
+ "edc_screening.view_screening_listboard",
28
+ "edc_screening.nav_screening_section",
29
+ name=SCREENING,
30
+ )
31
+
32
+ site_auths.add_group(
33
+ "edc_screening.view_screening_listboard",
34
+ "edc_screening.nav_screening_section",
35
+ name=SCREENING_SUPER,
36
+ )
37
+
38
+ site_auths.add_group(
39
+ "edc_screening.view_screening_listboard",
40
+ "edc_screening.nav_screening_section",
41
+ name=SCREENING_VIEW,
42
+ )
43
+
44
+ site_auths.add_role(SCREENING, name=SCREENING_ROLE)
45
+ site_auths.update_role(SCREENING, name=CLINICIAN_ROLE)
46
+ site_auths.update_role(SCREENING, name=NURSE_ROLE)
47
+ site_auths.update_role(SCREENING_SUPER, name=CLINICIAN_SUPER_ROLE)
48
+ site_auths.update_role(SCREENING_VIEW, name=AUDITOR_ROLE)
35
49
 
36
- site_auths.add_group(
37
- "edc_screening.view_screening_listboard",
38
- "edc_screening.nav_screening_section",
39
- name=SCREENING_VIEW,
40
- )
41
50
 
42
- site_auths.add_role(SCREENING, name=SCREENING_ROLE)
43
- site_auths.update_role(SCREENING, name=CLINICIAN_ROLE)
44
- site_auths.update_role(SCREENING, name=NURSE_ROLE)
45
- site_auths.update_role(SCREENING_SUPER, name=CLINICIAN_SUPER_ROLE)
46
- site_auths.update_role(SCREENING_VIEW, name=AUDITOR_ROLE)
51
+ update_site_auths()
@@ -45,7 +45,7 @@ class Eligibility:
45
45
  self.eligible = all([v for v in self.criteria.values()])
46
46
 
47
47
  if self.eligible:
48
- self.reasons_ineligible = None
48
+ self.reasons_ineligible = ""
49
49
  else:
50
50
  self.reasons_ineligible = {k: v for k, v in self.criteria.items() if not v}
51
51
  for k, v in self.criteria.items():
@@ -6,7 +6,7 @@ class GenderEvaluator:
6
6
 
7
7
  def __init__(self, gender=None, **kwargs) -> None: # noqa
8
8
  self.eligible = False
9
- self.reasons_ineligible = None
9
+ self.reasons_ineligible = ""
10
10
  if gender in self.eligible_gender:
11
11
  self.eligible = True
12
12
  else:
@@ -45,8 +45,6 @@ class EligibilityModelMixin(EligibilityFieldsModelMixin, models.Model):
45
45
  """
46
46
  # if self.eligibility_cls:
47
47
  self.eligibility_cls(model_obj=self)
48
- # self.eligible = eligibility_obj.is_eligible
49
- # self.reasons_ineligible = eligibility_obj.reasons_ineligible
50
48
  if not self.id:
51
49
  self.screening_identifier = self.identifier_cls().identifier
52
50
  if self.eligible:
@@ -16,7 +16,7 @@ class ScreeningMethodsModeMixin(models.Model):
16
16
 
17
17
  @staticmethod
18
18
  def get_search_slug_fields():
19
- return ["screening_identifier", "subject_identifier", "reference"]
19
+ return "screening_identifier", "subject_identifier", "reference"
20
20
 
21
21
  @property
22
22
  def estimated_dob(self: SubjectScreeningModelStub) -> date:
@@ -174,7 +174,7 @@ class ScreeningEligibility:
174
174
  setattr(
175
175
  self.model_obj,
176
176
  self.reasons_ineligible_fld_name,
177
- "|".join(self.reasons_ineligible.values()) or None,
177
+ "|".join(self.reasons_ineligible.values()) or "",
178
178
  )
179
179
  self.set_eligible_model_field()
180
180
  self.set_fld_attrs_on_model()
@@ -229,9 +229,7 @@ class ScreeningEligibility:
229
229
  }
230
230
 
231
231
  def formatted_reasons_ineligible(self) -> str:
232
- str_values = "<BR>".join(
233
- [x for x in self.reasons_ineligible.values() if x is not None]
234
- )
232
+ str_values = "<BR>".join([x for x in self.reasons_ineligible.values() if x])
235
233
  return format_html(
236
234
  "{}",
237
235
  mark_safe(str_values), # nosec B703 B308
edc_screening/utils.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import re
4
5
  from typing import TYPE_CHECKING, Any
5
6
 
@@ -33,14 +34,15 @@ def get_subject_screening_model_cls() -> Any:
33
34
  return django_apps.get_model(get_subject_screening_model())
34
35
 
35
36
 
36
- def format_reasons_ineligible(*str_values: str, delimiter=None) -> str:
37
+ def format_reasons_ineligible(*str_values: str | None, delimiter: str | None = None) -> str:
37
38
  reasons = None
38
39
  delimiter = delimiter or "|"
39
- str_values = tuple(x for x in str_values if x is not None)
40
+ str_values = str_values or []
41
+ str_values = tuple(x for x in str_values if x)
40
42
  if str_values:
41
43
  reasons = format_html(
42
44
  "{}",
43
- mark_safe(delimiter.join(str_values)), # nosec B703 B308
45
+ mark_safe(delimiter.join(str_values)), # noqa: S308
44
46
  )
45
47
  return reasons
46
48
 
@@ -73,10 +75,10 @@ def get_subject_screening_or_raise(
73
75
  )
74
76
  except ObjectDoesNotExist as e:
75
77
  if is_modelform:
76
- raise forms.ValidationError("Not allowed. Screening form not found.")
78
+ raise forms.ValidationError("Not allowed. Screening form not found.") from e
77
79
  raise ObjectDoesNotExist(
78
80
  f"{e} screening_identifier={screening_identifier}. Perhaps catch this in the form."
79
- )
81
+ ) from e
80
82
  return subject_screening
81
83
 
82
84
 
@@ -101,10 +103,8 @@ def is_eligible_or_raise(
101
103
  )
102
104
 
103
105
  url_name = url_name or "screening_listboard_url"
104
- try:
106
+ with contextlib.suppress(InvalidDashboardUrlName):
105
107
  url_name = url_names.get(url_name)
106
- except InvalidDashboardUrlName:
107
- pass
108
108
 
109
109
  if not subject_screening.eligible:
110
110
  try:
@@ -128,7 +128,7 @@ def is_eligible_or_raise(
128
128
  else:
129
129
  msg = format_html(
130
130
  'Not allowed. Subject is not eligible. See subject <A href="{}">{}</A>',
131
- mark_safe(url), # nosec B308 B703
131
+ mark_safe(url), # noqa: S308
132
132
  subject_screening.screening_identifier,
133
133
  )
134
134
  raise forms.ValidationError(msg)