clinicedc 2.0.7__py3-none-any.whl → 2.0.9__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 (142) hide show
  1. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/METADATA +4 -3
  2. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/RECORD +136 -137
  3. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.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/migrations/0036_alter_userprofile_alternate_email_and_more.py +88 -0
  27. edc_auth/models/user_profile.py +14 -11
  28. edc_auth/site_auths.py +80 -67
  29. edc_consent/auths.py +18 -12
  30. edc_constants/constants.py +1 -0
  31. edc_crf/auths.py +5 -0
  32. edc_dashboard/auths.py +10 -6
  33. edc_dashboard/url_config.py +92 -83
  34. edc_dashboard/url_names.py +4 -4
  35. edc_dashboard/view_mixins/url_request_context_mixin.py +6 -5
  36. edc_data_manager/admin/data_query_admin.py +12 -11
  37. edc_data_manager/auths.py +37 -34
  38. edc_data_manager/rule/query_rule_wrapper.py +7 -7
  39. edc_export/archive_exporter.py +3 -2
  40. edc_export/auths.py +32 -28
  41. edc_export/model_exporter/model_exporter.py +4 -1
  42. edc_facility/auths.py +8 -3
  43. edc_facility/facility.py +8 -9
  44. edc_form_label/custom_label_condition.py +11 -8
  45. edc_form_label/form_label.py +1 -1
  46. edc_form_runners/auths.py +11 -6
  47. edc_form_validators/applicable_field_validator.py +7 -6
  48. edc_form_validators/base_form_validator.py +8 -9
  49. edc_form_validators/other_specify_field_validator.py +2 -8
  50. edc_form_validators/required_field_validator.py +19 -16
  51. edc_identifier/research_identifier.py +11 -10
  52. edc_identifier/simple_identifier.py +8 -2
  53. edc_lab/auths.py +26 -23
  54. edc_lab/lab/aliquot_creator.py +5 -8
  55. edc_lab/lab/primary_aliquot.py +14 -5
  56. edc_lab/migrations/0038_alter_aliquot_slug_alter_box_slug_alter_boxitem_slug_and_more.py +112 -0
  57. edc_lab/model_mixins/requisition/requisition_model_mixin.py +6 -8
  58. edc_lab/models/aliquot.py +2 -2
  59. edc_lab/models/manifest/manifest.py +2 -2
  60. edc_lab/models/manifest/manifest_item.py +1 -1
  61. edc_lab_dashboard/auths.py +16 -11
  62. edc_lab_results/calculate_missing.py +8 -8
  63. edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +2 -2
  64. edc_lab_results/get_summary.py +26 -25
  65. edc_lab_results/model_mixins/blood_result_model_mixin.py +2 -0
  66. edc_label/auths.py +6 -1
  67. edc_label/label_template.py +8 -8
  68. edc_list_data/load_model_data.py +3 -3
  69. edc_list_data/post_migrate_signals.py +1 -1
  70. edc_list_data/preload_data.py +2 -2
  71. edc_list_data/row.py +1 -1
  72. edc_list_data/site_list_data.py +6 -5
  73. edc_locator/auths.py +18 -13
  74. edc_metadata/auths.py +11 -7
  75. edc_metadata/metadata/metadata.py +1 -1
  76. edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
  77. edc_metadata/metadata_rules/metadata_rule_evaluator.py +5 -3
  78. edc_metadata/metadata_rules/rule.py +2 -3
  79. edc_metadata/metadata_rules/rule_evaluator.py +1 -1
  80. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +7 -4
  81. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +5 -2
  82. edc_metadata/models/signals.py +10 -11
  83. edc_navbar/auths.py +18 -13
  84. edc_notification/auths.py +9 -4
  85. edc_notification/notification/graded_event_notification.py +2 -2
  86. edc_notification/notification/model_notification.py +3 -30
  87. edc_notification/notification/new_model_notification.py +1 -1
  88. edc_notification/notification/notification.py +1 -1
  89. edc_notification/notification/updated_model_notification.py +2 -2
  90. edc_offstudy/auths.py +12 -7
  91. edc_pdutils/df_exporters/csv_model_exporter.py +5 -2
  92. edc_pharmacy/auths.py +19 -15
  93. edc_pharmacy/models/medication/formulation.py +5 -7
  94. edc_pharmacy/prescribe/create_prescription.py +3 -3
  95. edc_pharmacy/utils/confirm_stock.py +1 -1
  96. edc_pharmacy/utils/confirm_stock_at_site.py +1 -1
  97. edc_pharmacy/views/confirmation_at_site_view.py +6 -9
  98. edc_prn/admin_site.py +5 -0
  99. edc_prn/prn.py +10 -11
  100. edc_prn/urls.py +11 -0
  101. edc_protocol_incident/action_items.py +4 -4
  102. edc_protocol_incident/auths.py +27 -20
  103. edc_pylabels/auths.py +6 -1
  104. edc_qareports/auths.py +11 -7
  105. edc_randomization/admin.py +30 -24
  106. edc_randomization/auths.py +12 -7
  107. edc_randomization/randomizer.py +22 -20
  108. edc_randomization/utils.py +17 -16
  109. edc_refusal/auths.py +7 -2
  110. edc_refusal/model_mixins.py +1 -1
  111. edc_registration/auths.py +28 -23
  112. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +13 -4
  113. edc_registration/models/registered_subject.py +1 -1
  114. edc_reportable/utils/get_reference_range_collection.py +2 -3
  115. edc_reportable/utils/load_data.py +1 -1
  116. edc_review_dashboard/auths.py +23 -18
  117. edc_screening/age_evaluator.py +3 -3
  118. edc_screening/auths.py +35 -30
  119. edc_screening/eligibility.py +1 -1
  120. edc_screening/gender_evaluator.py +1 -1
  121. edc_screening/model_mixins/eligibility_model_mixin.py +0 -2
  122. edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
  123. edc_screening/screening_eligibility.py +2 -4
  124. edc_screening/utils.py +9 -9
  125. edc_search/generate_slug.py +26 -0
  126. edc_search/model_mixins.py +10 -21
  127. edc_sites/auths.py +8 -3
  128. edc_subject_dashboard/auths.py +27 -22
  129. edc_timepoint/apps.py +0 -21
  130. edc_unblinding/auths.py +9 -4
  131. edc_utils/__init__.py +3 -1
  132. edc_utils/show_urls.py +29 -2
  133. edc_visit_schedule/auths.py +6 -1
  134. edc_visit_schedule/site_visit_schedules.py +2 -2
  135. edc_visit_tracking/models/signals.py +2 -2
  136. edc_form_label/models.py +0 -0
  137. edc_search/constants.py +0 -1
  138. edc_search/models.py +0 -0
  139. edc_search/search_slug.py +0 -51
  140. edc_search/updater.py +0 -30
  141. edc_search/wsgi.py +0 -7
  142. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/licenses/LICENSE +0 -0
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)
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from django.utils.text import slugify
4
+ from django_crypto_fields.utils import get_encrypted_field_names
5
+
6
+ SLUG_SEP = "|"
7
+
8
+
9
+ def generate_slug(obj, fields) -> str | None:
10
+ """Returns a slug of the char values of the fields
11
+ listed on the models get_search_slug_fields() method.
12
+
13
+ Excludes any encrypted fields if listed.
14
+ """
15
+ slug = None
16
+ if obj and fields:
17
+ values = []
18
+ for field in (f for f in fields if f not in get_encrypted_field_names(obj)):
19
+ v = obj
20
+ for f in field.split("."):
21
+ v = getattr(v, f)
22
+ if isinstance(v, str):
23
+ values.append(v[:50]) # truncate value
24
+ slugs = [slugify(v) for v in list(set(values)) if v]
25
+ slug = SLUG_SEP.join(slugs)[:250]
26
+ return slug
@@ -1,42 +1,31 @@
1
1
  from django.db import models
2
+ from tqdm import tqdm
2
3
 
3
- from .updater import SearchSlugUpdater
4
+ from .generate_slug import generate_slug
4
5
 
5
6
 
6
7
  class SearchSlugManager(models.Manager):
7
- search_slug_updater_cls = SearchSlugUpdater
8
- search_slug_field_name = "slug"
9
-
10
8
  def update_search_slugs(self) -> None:
11
- for obj in self.all():
12
- updater = self.search_slug_updater_cls(
13
- fields=obj.get_search_slug_fields(), model_obj=obj
14
- )
15
- setattr(obj, self.search_slug_field_name, updater.slug)
16
- obj.save_base(update_fields=[self.search_slug_field_name])
9
+ qs = self.all()
10
+ for obj in tqdm(qs, total=qs.count()):
11
+ obj.slug = generate_slug(obj, obj.get_search_slug_fields()) or ""
12
+ obj.save_base(update_fields=["slug"])
17
13
 
18
14
 
19
15
  class SearchSlugModelMixin(models.Model):
20
- search_slug_warning = None
21
- search_slug_updater_cls = SearchSlugUpdater
22
-
23
- def get_search_slug_fields(self) -> tuple[str] | None:
24
- return None
16
+ def get_search_slug_fields(self) -> tuple[str, ...]:
17
+ return ()
25
18
 
26
19
  slug = models.CharField(
27
20
  max_length=250,
28
21
  default="",
29
22
  editable=False,
30
23
  db_index=True,
31
- help_text="a field used for quick search",
24
+ help_text="Hold slug field values for quick search. Excludes encrypted fields",
32
25
  )
33
26
 
34
27
  def save(self, *args, **kwargs):
35
- updater = self.search_slug_updater_cls(
36
- fields=self.get_search_slug_fields(), model_obj=self
37
- )
38
- self.search_slug_warning = updater.warning
39
- self.slug = updater.slug
28
+ self.slug = generate_slug(self, self.get_search_slug_fields()) or ""
40
29
  super().save(*args, **kwargs)
41
30
 
42
31
  class Meta:
edc_sites/auths.py CHANGED
@@ -1,6 +1,11 @@
1
1
  from edc_auth.site_auths import site_auths
2
2
  from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
3
3
 
4
- site_auths.add_post_update_func(
5
- "edc_sites", remove_default_model_permissions_from_edc_permissions
6
- )
4
+
5
+ def update_site_auths() -> None:
6
+ site_auths.add_post_update_func(
7
+ "edc_sites", remove_default_model_permissions_from_edc_permissions
8
+ )
9
+
10
+
11
+ update_site_auths()
@@ -7,30 +7,35 @@ from edc_auth.constants import (
7
7
  from edc_auth.site_auths import site_auths
8
8
  from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
9
9
 
10
- site_auths.add_post_update_func(
11
- "edc_subject_dashboard", remove_default_model_permissions_from_edc_permissions
12
- )
13
-
14
10
  SUBJECT_VIEW = "SUBJECT_VIEW"
15
11
 
16
- site_auths.add_custom_permissions_tuples(
17
- model="edc_subject_dashboard.edcpermissions",
18
- codename_tuples=(
19
- (
20
- "edc_subject_dashboard.view_subject_listboard",
21
- "Can access subject listboard",
12
+
13
+ def update_site_auths() -> None:
14
+ site_auths.add_post_update_func(
15
+ "edc_subject_dashboard", remove_default_model_permissions_from_edc_permissions
16
+ )
17
+
18
+ site_auths.add_custom_permissions_tuples(
19
+ model="edc_subject_dashboard.edcpermissions",
20
+ codename_tuples=(
21
+ (
22
+ "edc_subject_dashboard.view_subject_listboard",
23
+ "Can access subject listboard",
24
+ ),
25
+ ("edc_subject_dashboard.nav_subject_section", "Can access nav_subject_section"),
22
26
  ),
23
- ("edc_subject_dashboard.nav_subject_section", "Can access nav_subject_section"),
24
- ),
25
- )
27
+ )
28
+
29
+ site_auths.add_group(
30
+ "edc_subject_dashboard.view_subject_listboard",
31
+ "edc_subject_dashboard.nav_subject_section",
32
+ name=SUBJECT_VIEW,
33
+ )
34
+
35
+ site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_ROLE)
36
+ site_auths.update_role(SUBJECT_VIEW, name=NURSE_ROLE)
37
+ site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_SUPER_ROLE)
38
+ site_auths.update_role(SUBJECT_VIEW, name=AUDITOR_ROLE)
26
39
 
27
- site_auths.add_group(
28
- "edc_subject_dashboard.view_subject_listboard",
29
- "edc_subject_dashboard.nav_subject_section",
30
- name=SUBJECT_VIEW,
31
- )
32
40
 
33
- site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_ROLE)
34
- site_auths.update_role(SUBJECT_VIEW, name=NURSE_ROLE)
35
- site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_SUPER_ROLE)
36
- site_auths.update_role(SUBJECT_VIEW, name=AUDITOR_ROLE)
41
+ update_site_auths()
edc_timepoint/apps.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import sys
2
2
 
3
3
  from django.apps import AppConfig as DjangoAppConfig
4
- from django.conf import settings
5
4
 
6
5
  from edc_appointment.constants import COMPLETE_APPT
7
6
 
@@ -35,23 +34,3 @@ class AppConfig(DjangoAppConfig):
35
34
  for model in self.timepoints:
36
35
  sys.stdout.write(f" * '{model}' is a timepoint model.\n")
37
36
  sys.stdout.write(f" Done loading {self.verbose_name}.\n")
38
-
39
-
40
- if settings.APP_NAME == "edc_timepoint":
41
- from dateutil.relativedelta import FR, MO, SA, SU, TH, TU, WE
42
-
43
- from edc_facility.apps import AppConfig as BaseEdcFacilityAppConfig
44
-
45
- class EdcFacilityAppConfig(BaseEdcFacilityAppConfig):
46
- definitions = {
47
- "7-day-clinic": dict(
48
- days=[MO, TU, WE, TH, FR, SA, SU],
49
- slots=[100, 100, 100, 100, 100, 100, 100],
50
- ),
51
- "5-day-clinic": dict(days=[MO, TU, WE, TH, FR], slots=[100, 100, 100, 100, 100]),
52
- "3-day-clinic": dict(
53
- days=[TU, WE, TH],
54
- slots=[100, 100, 100],
55
- best_effort_available_datetime=True,
56
- ),
57
- }
edc_unblinding/auths.py CHANGED
@@ -9,7 +9,12 @@ from .auth_objects import (
9
9
  unblinding_reviewers,
10
10
  )
11
11
 
12
- site_auths.add_group(*unblinding_requestors, name=UNBLINDING_REQUESTORS)
13
- site_auths.add_group(*unblinding_reviewers, name=UNBLINDING_REVIEWERS)
14
- site_auths.add_role(UNBLINDING_REQUESTORS, name=UNBLINDING_REQUESTORS_ROLE)
15
- site_auths.add_role(UNBLINDING_REVIEWERS, name=UNBLINDING_REVIEWERS_ROLE)
12
+
13
+ def update_site_auths() -> None:
14
+ site_auths.add_group(*unblinding_requestors, name=UNBLINDING_REQUESTORS)
15
+ site_auths.add_group(*unblinding_reviewers, name=UNBLINDING_REVIEWERS)
16
+ site_auths.add_role(UNBLINDING_REQUESTORS, name=UNBLINDING_REQUESTORS_ROLE)
17
+ site_auths.add_role(UNBLINDING_REVIEWERS, name=UNBLINDING_REVIEWERS_ROLE)
18
+
19
+
20
+ update_site_auths()
edc_utils/__init__.py CHANGED
@@ -8,7 +8,7 @@ from .get_static_file import get_static_file
8
8
  from .get_uuid import get_uuid
9
9
  from .message_in_queue import message_in_queue
10
10
  from .round_up import round_half_away_from_zero, round_half_up, round_up
11
- from .show_urls import show_url_names, show_urls
11
+ from .show_urls import show_namespaces, show_url_names, show_urls, show_urls_from_patterns
12
12
  from .text import (
13
13
  convert_from_camel,
14
14
  convert_php_dateformat,
@@ -48,8 +48,10 @@ __all__ = [
48
48
  "round_half_up",
49
49
  "round_up",
50
50
  "safe_allowed_chars",
51
+ "show_namespaces",
51
52
  "show_url_names",
52
53
  "show_urls",
54
+ "show_urls_from_patterns",
53
55
  "to_local",
54
56
  "to_utc",
55
57
  "truncate_string",