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
@@ -30,10 +30,11 @@ class UrlRequestContextMixin:
30
30
  @classmethod
31
31
  def urls(
32
32
  cls,
33
- namespace: str = None,
34
- label: str = None,
35
- identifier_label: str = None,
36
- identifier_pattern: str = None,
33
+ *,
34
+ label: str,
35
+ identifier_pattern: str,
36
+ namespace: str | None = None,
37
+ identifier_label: str | None = None,
37
38
  ) -> list[URLPattern]:
38
39
  label = (
39
40
  label
@@ -62,5 +63,5 @@ class UrlRequestContextMixin:
62
63
  f"Url name not defined in url_names. "
63
64
  f"Expected one of {url_names.registry}. Got {e}. "
64
65
  f"Hint: check if dashboard middleware is loaded."
65
- )
66
+ ) from e
66
67
  return url_data
@@ -45,7 +45,7 @@ class DataQueryAdmin(SiteModelAdminMixin, ModelAdminSubjectDashboardMixin, Simpl
45
45
 
46
46
  locked_column_template_name = "edc_data_manager/columns/locked.html"
47
47
 
48
- status_column_context = {
48
+ status_column_context = { # noqa: RUF012
49
49
  "NEW": NEW,
50
50
  "OPEN": OPEN,
51
51
  "FEEDBACK": FEEDBACK,
@@ -61,13 +61,13 @@ class DataQueryAdmin(SiteModelAdminMixin, ModelAdminSubjectDashboardMixin, Simpl
61
61
 
62
62
  actions = (toggle_dm_status,)
63
63
 
64
- radio_fields = {
64
+ radio_fields = { # noqa: RUF012
65
65
  "status": admin.VERTICAL,
66
66
  "site_response_status": admin.VERTICAL,
67
67
  "query_priority": admin.VERTICAL,
68
68
  }
69
69
 
70
- autocomplete_fields = [
70
+ autocomplete_fields = (
71
71
  "sender",
72
72
  "recipients",
73
73
  "data_dictionaries",
@@ -75,7 +75,7 @@ class DataQueryAdmin(SiteModelAdminMixin, ModelAdminSubjectDashboardMixin, Simpl
75
75
  "visit_schedule",
76
76
  "registered_subject",
77
77
  "dm_user",
78
- ]
78
+ )
79
79
 
80
80
  list_display = (
81
81
  "wrapped_title",
@@ -201,11 +201,10 @@ class DataQueryAdmin(SiteModelAdminMixin, ModelAdminSubjectDashboardMixin, Simpl
201
201
  wrapped_title.short_description = "Title"
202
202
 
203
203
  def formfield_for_manytomany(self, db_field, request, **kwargs):
204
- if db_field.name == "data_dictionaries":
205
- if request.GET.get("title"):
206
- kwargs["queryset"] = DataDictionary.objects.filter(
207
- model_verbose_name=request.GET.get("title")
208
- )
204
+ if db_field.name == "data_dictionaries" and request.GET.get("title"):
205
+ kwargs["queryset"] = DataDictionary.objects.filter(
206
+ model_verbose_name=request.GET.get("title")
207
+ )
209
208
  return super().formfield_for_manytomany(db_field, request, **kwargs)
210
209
 
211
210
  def query_dates(self, obj):
@@ -332,7 +331,8 @@ class DataQueryAdmin(SiteModelAdminMixin, ModelAdminSubjectDashboardMixin, Simpl
332
331
  "missed_visit",
333
332
  "auto_resolved",
334
333
  "registered_subject",
335
- ) + action_fields
334
+ *action_fields,
335
+ )
336
336
  if not request.user.groups.filter(name=DATA_MANAGER):
337
337
  extra_fields = (
338
338
  "data_dictionaries",
@@ -350,10 +350,11 @@ class DataQueryAdmin(SiteModelAdminMixin, ModelAdminSubjectDashboardMixin, Simpl
350
350
  "title",
351
351
  "visit_code_sequence",
352
352
  "visit_schedule",
353
+ *extra_fields,
353
354
  )
354
355
  if not obj:
355
356
  extra_fields = tuple(f for f in extra_fields if f != "registered_subject")
356
- return fields + extra_fields
357
+ return *fields, *extra_fields
357
358
 
358
359
  def get_subject_dashboard_url_kwargs(self, obj):
359
360
  def get_opts():
edc_data_manager/auths.py CHANGED
@@ -18,45 +18,48 @@ from .auth_objects import (
18
18
  data_manager,
19
19
  )
20
20
 
21
- site_auths.add_custom_permissions_tuples(
22
- model="edc_data_manager.edcpermissions", codename_tuples=custom_codename_tuples
23
- )
24
21
 
25
- site_auths.add_custom_permissions_tuples(
26
- model="edc_data_manager.edcpermissions",
27
- codename_tuples=[("edc_data_manager.special_bypassmodelform", "Can bypass modelform")],
28
- )
22
+ def update_site_auths():
23
+ site_auths.add_custom_permissions_tuples(
24
+ model="edc_data_manager.edcpermissions", codename_tuples=custom_codename_tuples
25
+ )
29
26
 
27
+ site_auths.add_custom_permissions_tuples(
28
+ model="edc_data_manager.edcpermissions",
29
+ codename_tuples=[("edc_data_manager.special_bypassmodelform", "Can bypass modelform")],
30
+ )
30
31
 
31
- # groups
32
- site_auths.add_group(*data_manager, name=DATA_MANAGER)
33
- site_auths.add_group(*data_manager, name=DATA_QUERY_VIEW, view_only=True)
32
+ # groups
33
+ site_auths.add_group(*data_manager, name=DATA_MANAGER)
34
+ site_auths.add_group(*data_manager, name=DATA_QUERY_VIEW, view_only=True)
34
35
 
35
- site_auths.add_group(*data_manager, name=DATA_QUERY, view_only=True)
36
- site_auths.update_group("edc_data_manager.change_dataquery", name=DATA_QUERY)
36
+ site_auths.add_group(*data_manager, name=DATA_QUERY, view_only=True)
37
+ site_auths.update_group("edc_data_manager.change_dataquery", name=DATA_QUERY)
37
38
 
38
- site_auths.add_group(
39
- "edc_data_manager.export_datadictionary",
40
- "edc_data_manager.export_dataquery",
41
- "edc_data_manager.export_queryrule",
42
- name=DATA_MANAGER_EXPORT,
43
- )
39
+ site_auths.add_group(
40
+ "edc_data_manager.export_datadictionary",
41
+ "edc_data_manager.export_dataquery",
42
+ "edc_data_manager.export_queryrule",
43
+ name=DATA_MANAGER_EXPORT,
44
+ )
44
45
 
45
- site_auths.add_group(*data_manager, name=DATA_MANAGER_SUPER)
46
- site_auths.update_group("edc_data_manager.change_dataquery", name=DATA_MANAGER_SUPER)
47
- site_auths.update_group(
48
- "edc_data_manager.export_datadictionary",
49
- "edc_data_manager.export_dataquery",
50
- "edc_data_manager.export_queryrule",
51
- "edc_data_manager.special_bypassmodelform",
52
- "edc_data_manager.change_dataquery",
53
- name=DATA_MANAGER_SUPER,
54
- )
46
+ site_auths.add_group(*data_manager, name=DATA_MANAGER_SUPER)
47
+ site_auths.update_group("edc_data_manager.change_dataquery", name=DATA_MANAGER_SUPER)
48
+ site_auths.update_group(
49
+ "edc_data_manager.export_datadictionary",
50
+ "edc_data_manager.export_dataquery",
51
+ "edc_data_manager.export_queryrule",
52
+ "edc_data_manager.special_bypassmodelform",
53
+ "edc_data_manager.change_dataquery",
54
+ name=DATA_MANAGER_SUPER,
55
+ )
56
+
57
+ # roles
58
+ site_auths.add_role(CELERY_MANAGER, DATA_MANAGER, name=DATA_MANAGER_ROLE)
59
+ site_auths.add_role(DATA_QUERY, name=SITE_DATA_MANAGER_ROLE)
60
+ site_auths.update_role(DATA_QUERY, name=CLINICIAN_ROLE)
61
+ site_auths.update_role(DATA_QUERY, name=NURSE_ROLE)
62
+ site_auths.update_role(DATA_QUERY, name=CLINICIAN_SUPER_ROLE)
55
63
 
56
64
 
57
- # roles
58
- site_auths.add_role(CELERY_MANAGER, DATA_MANAGER, name=DATA_MANAGER_ROLE)
59
- site_auths.add_role(DATA_QUERY, name=SITE_DATA_MANAGER_ROLE)
60
- site_auths.update_role(DATA_QUERY, name=CLINICIAN_ROLE)
61
- site_auths.update_role(DATA_QUERY, name=NURSE_ROLE)
62
- site_auths.update_role(DATA_QUERY, name=CLINICIAN_SUPER_ROLE)
65
+ update_site_auths()
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from datetime import datetime
4
4
  from decimal import Decimal
5
- from typing import TYPE_CHECKING, LiteralString
5
+ from typing import TYPE_CHECKING
6
6
 
7
7
  from tqdm import tqdm
8
8
 
@@ -17,12 +17,12 @@ if TYPE_CHECKING:
17
17
  class QueryRuleWrapper:
18
18
  def __init__(
19
19
  self,
20
- query_rule_obj: QueryRule = None,
21
- subject_identifiers: list[LiteralString] = None,
22
- visit_schedule_obj: QueryVisitSchedule = None,
23
- timepoint: Decimal = None,
24
- entry_status: str = None,
25
- now: datetime = None,
20
+ query_rule_obj: QueryRule | None = None,
21
+ subject_identifiers: list[str] | None = None,
22
+ visit_schedule_obj: QueryVisitSchedule | None = None,
23
+ timepoint: Decimal | None = None,
24
+ entry_status: str | None = None,
25
+ now: datetime | None = None,
26
26
  verbose: bool | None = None,
27
27
  ):
28
28
  self.now = now
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from pathlib import Path
3
4
  from tempfile import mkdtemp
4
5
  from typing import TYPE_CHECKING
5
6
 
@@ -17,7 +18,7 @@ if TYPE_CHECKING:
17
18
  from django.contrib.auth.models import User
18
19
 
19
20
 
20
- class ArchiveExporterNothingExported(Exception):
21
+ class ArchiveExporterNothingExported(Exception): # noqa: N818
21
22
  pass
22
23
 
23
24
 
@@ -56,7 +57,7 @@ class ArchiveExporter:
56
57
  for model in models:
57
58
  csv_exporter = self.csv_exporter_cls(
58
59
  model=model,
59
- export_folder=tmp_folder,
60
+ export_folder=Path(tmp_folder),
60
61
  decrypt=decrypt,
61
62
  site_ids=sites.get_site_ids_for_user(
62
63
  user=user, site_id=sites.get_current_site().site_id
edc_export/auths.py CHANGED
@@ -4,34 +4,38 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
4
4
  from .auth_objects import export_codenames
5
5
  from .constants import DATA_EXPORTER_ROLE, EXPORT, EXPORT_PII
6
6
 
7
- site_auths.add_post_update_func(
8
- "edc_export",
9
- remove_default_model_permissions_from_edc_permissions,
10
- )
11
7
 
12
- site_auths.add_custom_permissions_tuples(
13
- model="edc_export.edcpermissions",
14
- codename_tuples=[
15
- (
16
- "edc_export.export_visitschedule",
17
- "Can export the visit schedule",
18
- ),
19
- (
20
- "edc_export.view_export_dashboard",
21
- "Can view export dashboard",
22
- ),
23
- (
24
- "edc_export.export_subjectschedulehistory",
25
- "Can export subject schedule history",
26
- ),
27
- (
28
- "edc_export.export_pii",
29
- "Can export PII",
30
- ),
31
- ],
32
- )
8
+ def update_site_auths():
9
+ site_auths.add_post_update_func(
10
+ "edc_export",
11
+ remove_default_model_permissions_from_edc_permissions,
12
+ )
33
13
 
14
+ site_auths.add_custom_permissions_tuples(
15
+ model="edc_export.edcpermissions",
16
+ codename_tuples=[
17
+ (
18
+ "edc_export.export_visitschedule",
19
+ "Can export the visit schedule",
20
+ ),
21
+ (
22
+ "edc_export.view_export_dashboard",
23
+ "Can view export dashboard",
24
+ ),
25
+ (
26
+ "edc_export.export_subjectschedulehistory",
27
+ "Can export subject schedule history",
28
+ ),
29
+ (
30
+ "edc_export.export_pii",
31
+ "Can export PII",
32
+ ),
33
+ ],
34
+ )
34
35
 
35
- site_auths.add_group(*export_codenames, name=EXPORT)
36
- site_auths.add_role(EXPORT, name=DATA_EXPORTER_ROLE)
37
- site_auths.add_group("edc_export.export_pii", name=EXPORT_PII)
36
+ site_auths.add_group(*export_codenames, name=EXPORT)
37
+ site_auths.add_role(EXPORT, name=DATA_EXPORTER_ROLE)
38
+ site_auths.add_group("edc_export.export_pii", name=EXPORT_PII)
39
+
40
+
41
+ update_site_auths()
@@ -102,7 +102,10 @@ class ModelExporter:
102
102
  if f in self.export_fields or f in self.audit_fields or f in self.required_fields:
103
103
  self._field_names.pop(self._field_names.index(f))
104
104
  self._field_names = (
105
- self.export_fields + self.required_fields + self.field_names + self.audit_fields
105
+ *self.export_fields,
106
+ *self.required_fields,
107
+ *self.field_names,
108
+ *self.audit_fields,
106
109
  )
107
110
 
108
111
  @property
edc_facility/auths.py CHANGED
@@ -2,6 +2,11 @@ from edc_auth.site_auths import site_auths
2
2
 
3
3
  from .auth_objects import EDC_FACILITY, EDC_FACILITY_SUPER, EDC_FACILITY_VIEW, codenames
4
4
 
5
- site_auths.add_group(*codenames, name=EDC_FACILITY_VIEW, view_only=True)
6
- site_auths.add_group(*codenames, name=EDC_FACILITY, no_delete=True)
7
- site_auths.add_group(*codenames, name=EDC_FACILITY_SUPER)
5
+
6
+ def update_site_auths():
7
+ site_auths.add_group(*codenames, name=EDC_FACILITY_VIEW, view_only=True)
8
+ site_auths.add_group(*codenames, name=EDC_FACILITY, no_delete=True)
9
+ site_auths.add_group(*codenames, name=EDC_FACILITY_SUPER)
10
+
11
+
12
+ update_site_auths()
edc_facility/facility.py CHANGED
@@ -7,8 +7,7 @@ from zoneinfo import ZoneInfo
7
7
 
8
8
  import arrow
9
9
  from arrow import Arrow
10
- from dateutil._common import weekday
11
- from dateutil.relativedelta import relativedelta
10
+ from dateutil.relativedelta import relativedelta, weekday
12
11
  from django.conf import settings
13
12
  from django.utils import timezone
14
13
 
@@ -36,19 +35,18 @@ class Facility:
36
35
  def __init__(
37
36
  self,
38
37
  name: str | None = None,
39
- days: list | None = None,
38
+ days: list[weekday] | None = None,
40
39
  slots: list[int] | None = None,
41
40
  best_effort_available_datetime: datetime | None = None,
42
41
  ):
43
- self.days = []
42
+ self.days = days
44
43
  self.name = name
45
44
  if not name:
46
45
  raise FacilityError(f"Name cannot be None. See {self!r}")
47
46
  self.best_effort_available_datetime = (
48
47
  True if best_effort_available_datetime is None else best_effort_available_datetime
49
48
  )
50
- for day in days:
51
- self.days.append(getattr(day, "weekday", weekday(day)))
49
+ self.weekdays = [d.weekday for d in self.days]
52
50
  self.slots = slots or [99999 for _ in self.days]
53
51
  self.config = dict(zip([str(d) for d in self.days], self.slots, strict=False))
54
52
  self.holidays = self.holiday_cls()
@@ -69,9 +67,9 @@ class Facility:
69
67
  slots_per_day = 0
70
68
  return slots_per_day
71
69
 
72
- @property
73
- def weekdays(self) -> list[int]:
74
- return [d.weekday for d in self.days]
70
+ # @property
71
+ # def weekdays(self) -> list[int]:
72
+ # return [d.weekday for d in self.days]
75
73
 
76
74
  @staticmethod
77
75
  def open_slot_on(arr) -> Arrow:
@@ -132,6 +130,7 @@ class Facility:
132
130
  reverse_delta=None,
133
131
  taken_datetimes=None,
134
132
  schedule_on_holidays=None,
133
+ **kwargs, # noqa: ARG002
135
134
  ):
136
135
  """Returns an arrow object for a datetime equal to or
137
136
  close to the suggested datetime.
@@ -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.apps import apps as django_apps
@@ -28,20 +29,24 @@ class CustomLabelCondition:
28
29
  """
29
30
  return
30
31
 
31
- def get_additional_options(self, request=None, obj=None, model=None):
32
+ def get_additional_options(self, request=None, obj=None, model=None): # noqa: ARG002
32
33
  return {}
33
34
 
34
35
  @property
35
36
  def appointment(self) -> Appointment | None:
36
37
  """Returns the appointment instance for this request or None."""
37
- return django_apps.get_model(self.appointment_model).objects.get(
38
- pk=self.request.GET.get("appointment")
39
- )
38
+ with contextlib.suppress(ObjectDoesNotExist):
39
+ return django_apps.get_model(self.appointment_model).objects.get(
40
+ pk=self.request.GET.get("appointment")
41
+ )
42
+ return None
40
43
 
41
44
  @property
42
45
  def previous_appointment(self) -> Appointment | None:
43
46
  """Returns the previous appointment for this request or None."""
44
- return self.appointment.previous_by_timepoint
47
+ with contextlib.suppress(ObjectDoesNotExist):
48
+ return self.appointment.previous_by_timepoint
49
+ return None
45
50
 
46
51
  @property
47
52
  def previous_visit(self):
@@ -73,10 +78,8 @@ class CustomLabelCondition:
73
78
  """
74
79
  previous_obj = None
75
80
  if self.previous_visit:
76
- try:
81
+ with contextlib.suppress(ObjectDoesNotExist):
77
82
  previous_obj = self.model.objects.get(
78
83
  **{f"{self.model.related_visit_model_attr()}": self.previous_visit}
79
84
  )
80
- except ObjectDoesNotExist:
81
- pass
82
85
  return previous_obj
@@ -41,5 +41,5 @@ class FormLabel:
41
41
  except KeyError as e:
42
42
  raise CustomFormLabelError(
43
43
  f"Custom label template has invalid keys. See {label}. Got {e}."
44
- )
44
+ ) from e
45
45
  return label
edc_form_runners/auths.py CHANGED
@@ -9,10 +9,15 @@ from .auth_objects import (
9
9
  codenames,
10
10
  )
11
11
 
12
- site_auths.add_group(*codenames, name=EDC_FORM_RUNNERS_VIEW, view_only=True)
13
- site_auths.add_group(*codenames, name=EDC_FORM_RUNNERS, no_delete=True)
14
- site_auths.add_group(*codenames, name=EDC_FORM_RUNNERS_SUPER)
15
12
 
16
- site_auths.update_role(EDC_FORM_RUNNERS_VIEW, name=AUDITOR_ROLE)
17
- site_auths.update_role(EDC_FORM_RUNNERS, name=CLINICIAN_ROLE)
18
- site_auths.update_role(EDC_FORM_RUNNERS_SUPER, name=DATA_MANAGER_ROLE)
13
+ def update_site_auths():
14
+ site_auths.add_group(*codenames, name=EDC_FORM_RUNNERS_VIEW, view_only=True)
15
+ site_auths.add_group(*codenames, name=EDC_FORM_RUNNERS, no_delete=True)
16
+ site_auths.add_group(*codenames, name=EDC_FORM_RUNNERS_SUPER)
17
+
18
+ site_auths.update_role(EDC_FORM_RUNNERS_VIEW, name=AUDITOR_ROLE)
19
+ site_auths.update_role(EDC_FORM_RUNNERS, name=CLINICIAN_ROLE)
20
+ site_auths.update_role(EDC_FORM_RUNNERS_SUPER, name=DATA_MANAGER_ROLE)
21
+
22
+
23
+ update_site_auths()
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from edc_constants.constants import NOT_APPLICABLE
3
+ from edc_constants.constants import NOT_APPLICABLE, NULL_STRING
4
4
 
5
5
  from .base_form_validator import (
6
6
  APPLICABLE_ERROR,
@@ -33,8 +33,8 @@ class ApplicableFieldValidator(BaseFormValidator):
33
33
  def applicable_if(
34
34
  self,
35
35
  *responses: Any,
36
- field: str = None,
37
- field_applicable: str = None,
36
+ field: str,
37
+ field_applicable: str,
38
38
  inverse: bool | None = None,
39
39
  is_instance_field: bool | None = None,
40
40
  msg: str | None = None,
@@ -95,8 +95,8 @@ class ApplicableFieldValidator(BaseFormValidator):
95
95
  def applicable(
96
96
  self,
97
97
  *responses: Any,
98
- field: str = None,
99
- field_applicable: str = None,
98
+ field: str,
99
+ field_applicable: str,
100
100
  inverse: bool | None = None,
101
101
  is_instance_field: bool | None = None,
102
102
  msg: str | None = None,
@@ -117,7 +117,8 @@ class ApplicableFieldValidator(BaseFormValidator):
117
117
  field_applicable_value = self.get(field_applicable)
118
118
 
119
119
  if field_value in responses and (
120
- field_applicable_value is None or field_applicable_value == not_applicable
120
+ field_applicable_value in (NULL_STRING, not_applicable)
121
+ or field_applicable_value is None
121
122
  ):
122
123
  self.raise_applicable(field_applicable, msg=msg, applicable_msg=applicable_msg)
123
124
  elif (
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  from copy import copy
4
5
  from typing import Any
5
6
 
6
7
  from django.core.exceptions import NON_FIELD_ERRORS
7
- from django.forms import ValidationError, forms
8
+ from django.forms import ValidationError
8
9
 
9
10
  APPLICABLE_ERROR = "applicable"
10
11
  INVALID_ERROR = "invalid"
@@ -14,7 +15,7 @@ REQUIRED_ERROR = "required"
14
15
  OUT_OF_RANGE_ERROR = "out_of_range"
15
16
 
16
17
 
17
- class InvalidModelFormFieldValidator(Exception):
18
+ class InvalidModelFormFieldValidator(Exception): # noqa: N818
18
19
  def __init__(self, message, code=None):
19
20
  message = f"Invalid field validator. Got '{message}'"
20
21
  super().__init__(message)
@@ -119,13 +120,13 @@ class BaseFormValidator:
119
120
  """
120
121
  try:
121
122
  self._clean()
122
- except forms.ValidationError as e:
123
+ except ValidationError as e:
123
124
  self.capture_error_message(e)
124
125
  self.capture_error_code(e)
125
- raise forms.ValidationError(e)
126
+ raise ValidationError(e) from e
126
127
  return self.cleaned_data
127
128
 
128
- def capture_error_message(self, e: forms.ValidationError) -> None:
129
+ def capture_error_message(self, e: ValidationError) -> None:
129
130
  try:
130
131
  self._errors.update(**e.error_dict)
131
132
  except AttributeError:
@@ -134,11 +135,9 @@ class BaseFormValidator:
134
135
  except AttributeError:
135
136
  self._errors.update({NON_FIELD_ERRORS: str(e)})
136
137
 
137
- def capture_error_code(self, e: forms.ValidationError) -> None:
138
- try:
138
+ def capture_error_code(self, e: ValidationError) -> None:
139
+ with contextlib.suppress(AttributeError):
139
140
  self._error_codes.append(e.code)
140
- except AttributeError:
141
- pass
142
141
 
143
142
  def get_inline_field_value(self, field=None, inline_set=None):
144
143
  """Returns the value of the first inline field that has a value."""
@@ -41,20 +41,14 @@ class OtherSpecifyFieldValidator(BaseFormValidator):
41
41
  cleaned_data.get(field), fk_stored_field_name, cleaned_data.get(field)
42
42
  )
43
43
 
44
- if (
45
- field_value is not None
46
- and field_value == other
47
- and not cleaned_data.get(other_specify_field)
48
- ):
44
+ if field_value and field_value == other and not cleaned_data.get(other_specify_field):
49
45
  ref = "" if not ref else f" ref: {ref}"
50
46
  message = {other_specify_field: required_msg or f"This field is required.{ref}"}
51
47
  self._errors.update(message)
52
48
  self._error_codes.append(REQUIRED_ERROR)
53
49
  raise ValidationError(message, code=REQUIRED_ERROR)
54
50
  if (
55
- field_value is not None
56
- and field_value != other
57
- and cleaned_data.get(other_specify_field)
51
+ field_value and field_value != other and cleaned_data.get(other_specify_field)
58
52
  ) or (field_value is None and cleaned_data.get(other_specify_field)):
59
53
  ref = "" if not ref else f" ref: {ref}"
60
54
  message = {