clinicedc 2.0.39__py3-none-any.whl → 2.0.41__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 (156) hide show
  1. {clinicedc-2.0.39.dist-info → clinicedc-2.0.41.dist-info}/METADATA +3 -12
  2. {clinicedc-2.0.39.dist-info → clinicedc-2.0.41.dist-info}/RECORD +145 -151
  3. {clinicedc-2.0.39.dist-info → clinicedc-2.0.41.dist-info}/WHEEL +1 -1
  4. edc_adverse_event/dashboard_urls.py +2 -0
  5. edc_adverse_event/middleware.py +7 -6
  6. edc_adverse_event/navbars.py +4 -8
  7. edc_adverse_event/urls.py +14 -6
  8. edc_adverse_event/view_mixins/ae/ae_listboard_view_mixin.py +6 -8
  9. edc_adverse_event/view_mixins/ae/death_report_listboard_view_mixin.py +2 -4
  10. edc_adverse_event/view_mixins/tmg/tmg_ae_listboard_view_mixin.py +2 -3
  11. edc_adverse_event/views/home_view.py +1 -2
  12. edc_adverse_event/views/tmg/death_listboard_view.py +8 -6
  13. edc_adverse_event/views/tmg/home_view.py +4 -3
  14. edc_adverse_event/views/tmg/summary_listboard_view.py +4 -4
  15. edc_appointment/utils.py +3 -6
  16. edc_appointment/views/unscheduled_appointment_view.py +1 -1
  17. edc_consent/form_validators/consent_definition_form_validator_mixin.py +5 -2
  18. edc_consent/model_mixins/consent_version_model_mixin.py +1 -1
  19. edc_consent/navbars.py +2 -1
  20. edc_crf/model_mixins/crf_model_mixin.py +5 -1
  21. edc_crf/model_mixins/crf_no_manager_model_mixin.py +2 -2
  22. edc_crf/model_mixins/singleton_crf_model_mixin.py +1 -1
  23. edc_dashboard/middleware.py +10 -16
  24. edc_dashboard/middleware_mixins.py +10 -0
  25. edc_dashboard/navbars.py +1 -1
  26. edc_dashboard/url_config.py +50 -31
  27. edc_dashboard/url_names.py +23 -17
  28. edc_dashboard/utils.py +4 -4
  29. edc_dashboard/view_mixins/template_request_context_mixin.py +5 -8
  30. edc_dashboard/view_mixins/url_request_context_mixin.py +38 -26
  31. edc_dashboard/views/administration_view.py +2 -2
  32. edc_dashboard/views/dashboard_view.py +5 -10
  33. edc_data_manager/handlers/handlers.py +17 -5
  34. edc_data_manager/migrations/0043_alter_historicalqueryrule_comment_and_more.py +51 -0
  35. edc_data_manager/models/query_rule.py +7 -7
  36. edc_data_manager/navbar_item.py +1 -1
  37. edc_data_manager/rule/query_rule_wrapper.py +1 -1
  38. edc_data_manager/rule/rule_runner.py +6 -6
  39. edc_device/navbars.py +1 -1
  40. edc_export/navbars.py +2 -2
  41. edc_glucose/model_mixin_factories/fasting_model_mixin_factory.py +1 -1
  42. edc_identifier/identifier.py +6 -9
  43. edc_lab_dashboard/dashboard_urls.py +7 -5
  44. edc_lab_dashboard/middleware.py +10 -17
  45. edc_lab_dashboard/navbars.py +9 -9
  46. edc_lab_dashboard/templates/edc_lab_dashboard/listboard/tags/status_column.html +7 -0
  47. edc_lab_dashboard/urls.py +2 -5
  48. edc_lab_dashboard/view_mixins/form_action_view_mixin.py +1 -2
  49. edc_lab_dashboard/views/action_views/action_view.py +6 -6
  50. edc_lab_dashboard/views/action_views/aliquot_view.py +1 -1
  51. edc_lab_dashboard/views/action_views/manage_box_item_view.py +2 -3
  52. edc_lab_dashboard/views/action_views/manage_manifest_view.py +1 -1
  53. edc_lab_dashboard/views/action_views/manifest_view.py +2 -2
  54. edc_lab_dashboard/views/action_views/pack_view.py +2 -2
  55. edc_lab_dashboard/views/action_views/process_view.py +1 -1
  56. edc_lab_dashboard/views/action_views/receive_view.py +1 -1
  57. edc_lab_dashboard/views/action_views/requisition_view.py +1 -1
  58. edc_lab_dashboard/views/action_views/verify_box_item_view.py +1 -1
  59. edc_lab_dashboard/views/listboard_views/manage_box_listboard_view.py +4 -5
  60. edc_lab_dashboard/views/listboard_views/manifest_listboard_view.py +5 -6
  61. edc_lab_dashboard/views/listboard_views/process_listboard_view.py +4 -5
  62. edc_lab_dashboard/views/listboard_views/receive_listboard_view.py +5 -6
  63. edc_lab_dashboard/views/listboard_views/verify_box_listboard_view.py +5 -6
  64. edc_label/navbars.py +1 -1
  65. edc_list_data/admin.py +3 -3
  66. edc_list_data/load_model_data.py +1 -1
  67. edc_list_data/management/commands/load_list_data.py +2 -2
  68. edc_list_data/site_list_data.py +4 -4
  69. edc_listboard/middleware.py +9 -8
  70. edc_listboard/templates/edc_listboard/listboard.html +1 -1
  71. edc_listboard/view_mixins/listboard_filter_view_mixin.py +1 -1
  72. edc_listboard/view_mixins/search_form_view_mixin.py +1 -1
  73. edc_listboard/views/listboard_view.py +16 -25
  74. edc_listboard/views/screen/screening_listboard_view.py +2 -2
  75. edc_listboard/views/subject/subject_listboard_view.py +2 -2
  76. edc_locator/forms/subject_locator_form_validator.py +2 -2
  77. edc_ltfu/action_items.py +1 -2
  78. edc_ltfu/forms/ltfu_form_validator_mixin.py +3 -3
  79. edc_ltfu/modeladmin_mixin.py +1 -1
  80. edc_ltfu/modelform_mixins.py +2 -2
  81. edc_metadata/admin/modeladmin_mixins.py +11 -9
  82. edc_metadata/management/commands/update_metadata.py +1 -1
  83. edc_metadata/management/commands/update_metadata_schedule_names.py +7 -7
  84. edc_metadata/management/commands/validate_entry_status.py +1 -1
  85. edc_metadata/management/commands/validate_rule_groups.py +1 -1
  86. edc_metadata/metadata/metadata_getter.py +3 -5
  87. edc_metadata/metadata_handler.py +5 -5
  88. edc_metadata/metadata_mixins/source_model_metadata_mixin.py +1 -1
  89. edc_metadata/metadata_refresher.py +1 -1
  90. edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
  91. edc_metadata/metadata_rules/logic.py +3 -3
  92. edc_metadata/metadata_rules/persistant_singleton_mixin.py +2 -4
  93. edc_metadata/metadata_rules/requisition/requisition_rule_group.py +1 -1
  94. edc_metadata/metadata_rules/rule.py +4 -3
  95. edc_metadata/metadata_rules/rule_group.py +2 -2
  96. edc_metadata/metadata_rules/rule_group_meta_options.py +2 -2
  97. edc_metadata/metadata_rules/rule_group_metaclass.py +21 -22
  98. edc_metadata/metadata_rules/site.py +1 -1
  99. edc_metadata/metadata_updater.py +4 -3
  100. edc_metadata/model_mixins/creates/creates_metadata_model_mixin.py +3 -5
  101. edc_metadata/model_mixins/updates/updates_metadata_model_mixin.py +1 -1
  102. edc_metadata/next_form_getter.py +15 -19
  103. edc_metadata/offline_models.py +1 -1
  104. edc_metadata/requisition/requisition_metadata_handler.py +5 -5
  105. edc_metadata/update_metadata_on_schedule_change.py +2 -4
  106. edc_metadata/utils.py +1 -1
  107. edc_model/models/signals.py +7 -2
  108. edc_model_admin/mixins/model_admin_redirect_on_delete_mixin.py +4 -3
  109. edc_navbar/apps.py +0 -2
  110. edc_navbar/navbar.py +1 -1
  111. edc_navbar/navbar_item.py +29 -16
  112. edc_navbar/navbars.py +6 -19
  113. edc_navbar/site_navbars.py +6 -7
  114. edc_navbar/system_checks.py +3 -10
  115. edc_navbar/utils.py +14 -0
  116. edc_navbar/view_mixin.py +6 -9
  117. edc_pharmacy/navbars.py +1 -1
  118. edc_pharmacy/views/confirm_stock_from_queryset_view.py +3 -3
  119. edc_protocol/middleware.py +9 -13
  120. edc_protocol/navbars.py +1 -1
  121. edc_refusal/forms.py +1 -3
  122. edc_reportable/utils/convert_units.py +1 -1
  123. edc_review_dashboard/middleware.py +6 -3
  124. edc_review_dashboard/navbars.py +1 -2
  125. edc_review_dashboard/urls.py +3 -2
  126. edc_review_dashboard/views/subject_review_listboard_view.py +4 -2
  127. edc_subject_dashboard/dashboard_templates.py +1 -3
  128. edc_subject_dashboard/dashboard_urls.py +8 -0
  129. edc_subject_dashboard/middleware.py +10 -7
  130. edc_subject_dashboard/templates/edc_subject_dashboard/buttons/refresh_appointments_button.html +1 -1
  131. edc_subject_dashboard/templates/edc_subject_dashboard/dashboard.html +1 -1
  132. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +3 -1
  133. edc_subject_dashboard/urls.py +13 -4
  134. edc_subject_dashboard/views/base_requisition_view.py +2 -1
  135. edc_subject_dashboard/views/subject_dashboard_view.py +1 -2
  136. edc_timepoint/__init__.py +0 -2
  137. edc_timepoint/model_mixins.py +1 -2
  138. edc_timepoint/utils.py +1 -1
  139. edc_timepoint/visit_timepoint_lookup.py +6 -0
  140. edc_visit_schedule/admin/subject_schedule_history_admin.py +1 -2
  141. edc_visit_schedule/navbars.py +3 -4
  142. edc_visit_schedule/visit/visit.py +15 -0
  143. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +5 -0
  144. edc_visit_tracking/models/subject_visit.py +5 -0
  145. edc_lab_dashboard/model_wrappers/__init__.py +0 -8
  146. edc_lab_dashboard/model_wrappers/aliquot_model_wrapper.py +0 -31
  147. edc_lab_dashboard/model_wrappers/base_box_item_model_wrapper.py +0 -21
  148. edc_lab_dashboard/model_wrappers/box_model_wrapper.py +0 -12
  149. edc_lab_dashboard/model_wrappers/manage_box_item_model_wrapper.py +0 -6
  150. edc_lab_dashboard/model_wrappers/manifest_item_model_wrapper.py +0 -21
  151. edc_lab_dashboard/model_wrappers/manifest_model_wrapper.py +0 -11
  152. edc_lab_dashboard/model_wrappers/requisition_model_wrapper.py +0 -25
  153. edc_lab_dashboard/model_wrappers/result_model_wrapper.py +0 -8
  154. edc_lab_dashboard/model_wrappers/verify_box_model_wrapper.py +0 -10
  155. edc_navbar/get_default_navbar.py +0 -9
  156. {clinicedc-2.0.39.dist-info → clinicedc-2.0.41.dist-info}/licenses/LICENSE +0 -0
@@ -1,24 +1,25 @@
1
1
  from django.conf import settings
2
2
 
3
+ from edc_dashboard.middleware_mixins import EdcTemplateMiddlewareMixin
4
+
3
5
  from .dashboard_templates import dashboard_templates
4
6
 
5
7
 
6
- class DashboardMiddleware:
8
+ class DashboardMiddleware(EdcTemplateMiddlewareMixin):
7
9
  def __init__(self, get_response):
8
10
  self.get_response = get_response
9
11
 
10
12
  def __call__(self, request):
13
+ self.check_for_required_request_attrs(request)
11
14
  return self.get_response(request)
12
15
 
13
- def process_view(self, request, *args) -> None:
14
- template_data = dashboard_templates
15
- try:
16
- template_data.update(settings.LISTBOARD_BASE_TEMPLATES)
17
- except AttributeError:
18
- pass
16
+ def process_view(self, request, *args):
17
+ template_data = getattr(settings, "LISTBOARD_BASE_TEMPLATES", {})
18
+ template_data.update(**dashboard_templates)
19
19
  request.template_data.update(**template_data)
20
20
 
21
21
  def process_template_response(self, request, response):
22
- if response.context_data:
22
+ if getattr(response, "context_data", None):
23
23
  response.context_data.update(**request.template_data)
24
+ request.template_data.update(**request.template_data)
24
25
  return response
@@ -1,4 +1,4 @@
1
- {% extends listboard_base_template %}
1
+ {% extends listboard_base_template|default:"edc_dashboard/base.html" %}
2
2
 
3
3
  {% load edc_dashboard_extras %}
4
4
  {% load tz %}
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
12
12
 
13
13
  class ListboardFilterViewMixin:
14
14
  listboard_view_filters = ListboardViewFilters()
15
- listboard_filter_url = None
15
+ listboard_filter_url = None # url name
16
16
 
17
17
  def __init__(self, **kwargs):
18
18
  self.listboard_view_exclude_filter_applied = False # TODO: ??
@@ -11,7 +11,7 @@ class SearchFormViewError(Exception):
11
11
 
12
12
 
13
13
  class SearchFormViewMixin:
14
- search_form_url = None
14
+ search_form_url = None # url_name in url_names dict
15
15
 
16
16
  def get_context_data(self, **kwargs) -> dict[str, Any]:
17
17
  kwargs.update(search_form_url_reversed=self.search_form_url_reversed)
@@ -8,6 +8,7 @@ from django.db.models import Q
8
8
  from django.utils.translation import gettext as _
9
9
  from django.views.generic.list import ListView
10
10
 
11
+ from edc_dashboard.url_names import url_names
11
12
  from edc_dashboard.view_mixins import (
12
13
  TemplateRequestContextMixin,
13
14
  UrlRequestContextMixin,
@@ -31,8 +32,8 @@ class BaseListboardView(SiteViewMixin, TemplateRequestContextMixin, ListView):
31
32
  listboard_template: str | None = None # an existing key in request.context_data
32
33
 
33
34
  # if self.listboard_url declared through another mixin.
34
- listboard_url: str | None = None # an existing key in request.context_data
35
- listboard_back_url: str | None = None
35
+ listboard_url: str | None = None # an existing key in request.context_data.url_names
36
+ listboard_back_url: str | None = None # see url_names, defaults to listboard_url
36
37
 
37
38
  # styling
38
39
  # default, info, success, danger, warning, etc. See Bootstrap.
@@ -65,32 +66,22 @@ class BaseListboardView(SiteViewMixin, TemplateRequestContextMixin, ListView):
65
66
  self.listboard_fa_icon = f"fas {self.listboard_fa_icon}"
66
67
  kwargs.update(
67
68
  empty_queryset_message=self.get_empty_queryset_message(),
69
+ has_listboard_model_perms=self.has_listboard_model_perms,
70
+ has_view_listboard_perms=self.has_view_listboard_perms,
68
71
  listboard_fa_icon=self.listboard_fa_icon,
72
+ listboard_instructions=self.listboard_instructions,
69
73
  listboard_panel_style=self.listboard_panel_style,
70
74
  listboard_panel_title=self.listboard_panel_title,
71
- listboard_instructions=self.listboard_instructions,
72
- show_change_form_button=self.show_change_form_button,
73
- # object_list=self.object_list,
74
- **self.add_url_to_context(
75
- new_key="listboard_url", existing_key=self.listboard_url
76
- ),
77
- )
78
- if self.listboard_back_url:
79
- kwargs.update(
80
- **self.add_url_to_context(
81
- new_key="listboard_back_url",
82
- existing_key=self.listboard_back_url,
83
- )
84
- )
85
- kwargs.update(
86
- has_listboard_model_perms=self.has_listboard_model_perms,
87
- has_view_listboard_perms=self.has_view_listboard_perms,
88
75
  listboard_view_permission_codename=self.listboard_view_permission_codename,
89
76
  permissions_warning_message=self.permissions_warning_message,
90
- **self.add_url_to_context(
91
- new_key="paginator_url",
92
- existing_key=self.paginator_url or self.listboard_url,
93
- ),
77
+ show_change_form_button=self.show_change_form_button,
78
+ **{"listboard_url": url_names.get(self.listboard_url)},
79
+ **{"paginator_url": url_names.get(self.paginator_url or self.listboard_url)},
80
+ **{
81
+ "listboard_back_url": url_names.get(
82
+ self.listboard_back_url or self.listboard_url
83
+ )
84
+ },
94
85
  )
95
86
  return super().get_context_data(**kwargs)
96
87
 
@@ -165,14 +156,14 @@ class BaseListboardView(SiteViewMixin, TemplateRequestContextMixin, ListView):
165
156
  queryset = queryset.order_by(*ordering)
166
157
  return queryset
167
158
 
168
- def get_queryset_filter_options(self, request, *args, **kwargs) -> tuple[Q, dict]:
159
+ def get_queryset_filter_options(self, request, *args, **kwargs) -> tuple[Q, dict]: # noqa: ARG002
169
160
  """Returns filtering applied to every queryset"""
170
161
  options = dict(site_id__in=sites.get_site_ids_for_user(request=self.request))
171
162
  if self.has_view_only_my_listboard_perms:
172
163
  options.update(user_created=self.request.user.username)
173
164
  return Q(), options
174
165
 
175
- def get_queryset_exclude_options(self, request, *args, **kwargs) -> tuple[Q, dict]:
166
+ def get_queryset_exclude_options(self, request, *args, **kwargs) -> tuple[Q, dict]: # noqa: ARG002
176
167
  """Returns exclude options applied to every queryset"""
177
168
  return Q(), {}
178
169
 
@@ -30,13 +30,13 @@ class ScreeningListboardView(
30
30
  ordering = "-report_datetime"
31
31
  paginate_by = 10
32
32
  search_form_url = "screening_listboard_url"
33
- search_fields = [
33
+ search_fields = (
34
34
  "screening_identifier",
35
35
  "initials__exact",
36
36
  "subject_identifier",
37
37
  "user_created",
38
38
  "user_modified",
39
- ]
39
+ )
40
40
 
41
41
  def get_context_data(self, **kwargs) -> dict:
42
42
  kwargs.update(
@@ -23,7 +23,7 @@ class SubjectListboardView(
23
23
  navbar_selected_item: str = "consented_subject"
24
24
  search_form_url: str = "subject_listboard_url"
25
25
 
26
- search_fields: list[str] = [
26
+ search_fields = (
27
27
  "user_created",
28
28
  "user_modified",
29
29
  "screening_identifier",
@@ -31,7 +31,7 @@ class SubjectListboardView(
31
31
  "initials__exact",
32
32
  "identity__exact",
33
33
  "first_name__exact",
34
- ]
34
+ )
35
35
 
36
36
  def get_listboard_model(self) -> str:
37
37
  return self.listboard_model
@@ -49,12 +49,12 @@ class SubjectLocatorFormValidator(FormValidator):
49
49
 
50
50
  def validate_may_call_fields(self):
51
51
  validations = {}
52
- number_fields = ["subject_cell", "subject_phone"]
52
+ number_fields = ("subject_cell", "subject_phone")
53
53
  if self.cleaned_data.get("may_call") == YES:
54
54
  if all([self.cleaned_data.get(f) is None for f in number_fields]):
55
55
  validations = {k: "This field is required" for k in number_fields}
56
56
  elif self.cleaned_data.get("may_call") == NO:
57
- number_fields.extend(["subject_cell_alt", "subject_phone_alt"]),
57
+ number_fields = {*number_fields, "subject_cell_alt", "subject_phone_alt"}
58
58
  for field in number_fields:
59
59
  if self.cleaned_data.get(field):
60
60
  validations.update({field: "This field is not required."})
edc_ltfu/action_items.py CHANGED
@@ -19,5 +19,4 @@ class LtfuAction(ActionWithNotification):
19
19
  priority = HIGH_PRIORITY
20
20
 
21
21
  def get_next_actions(self):
22
- next_actions = [END_OF_STUDY_ACTION]
23
- return next_actions
22
+ return [END_OF_STUDY_ACTION]
@@ -23,13 +23,13 @@ class LtfuFormValidatorMixin(FormValidator):
23
23
 
24
24
  try:
25
25
  self.ltfu_model_cls.objects.get(subject_identifier=subject_identifier)
26
- except ObjectDoesNotExist:
26
+ except ObjectDoesNotExist as e:
27
27
  if self.offschedule_reason_field not in self.cleaned_data:
28
28
  raise ImproperlyConfigured(
29
29
  "Unknown offschedule_reason_field. "
30
30
  f"Got '{self.offschedule_reason_field}'. "
31
31
  f"See form {self.__class__.__name__}"
32
- )
32
+ ) from e
33
33
  if self.cleaned_data.get(self.offschedule_reason_field) == LTFU:
34
34
  raise forms.ValidationError(
35
35
  {
@@ -39,4 +39,4 @@ class LtfuFormValidatorMixin(FormValidator):
39
39
  "form first."
40
40
  )
41
41
  }
42
- )
42
+ ) from e
@@ -46,7 +46,7 @@ class LtfuModelAdminMixin:
46
46
  "number_consecutive_missed_visits",
47
47
  )
48
48
 
49
- radio_fields = {
49
+ radio_fields = { # noqa: RUF012
50
50
  "home_visited": admin.VERTICAL,
51
51
  "ltfu_category": admin.VERTICAL,
52
52
  }
@@ -69,7 +69,7 @@ class RequiresLtfuFormValidatorMixin:
69
69
  ltfu = django_apps.get_model(self.ltfu_model).objects.get(
70
70
  subject_identifier=subject_identifier
71
71
  )
72
- except ObjectDoesNotExist:
72
+ except ObjectDoesNotExist as e:
73
73
  if (
74
74
  self.cleaned_data.get(self.offschedule_reason_field)
75
75
  and self.cleaned_data.get(self.offschedule_reason_field).name
@@ -80,7 +80,7 @@ class RequiresLtfuFormValidatorMixin:
80
80
  f"`{self.ltfu_model_cls._meta.verbose_name}` "
81
81
  "form first."
82
82
  )
83
- raise forms.ValidationError({self.offschedule_reason_field: msg})
83
+ raise forms.ValidationError({self.offschedule_reason_field: msg}) from e
84
84
  else:
85
85
  if self.cleaned_data.get(self.ltfu_date_field) and (
86
86
  ltfu.ltfu_date != self.cleaned_data.get(self.ltfu_date_field)
@@ -8,12 +8,8 @@ from django.utils.html import format_html
8
8
  from django.utils.safestring import mark_safe
9
9
  from django_audit_fields import ModelAdminAuditFieldsMixin, audit_fieldset_tuple
10
10
  from django_revision.modeladmin_mixin import ModelAdminRevisionMixin
11
- from rangefilter.filters import DateRangeFilterBuilder
12
-
13
11
  from edc_appointment.utils import get_appointment_model_cls
14
12
  from edc_dashboard.url_names import url_names
15
- from edc_metadata import KEYED, REQUIRED
16
- from edc_metadata.admin.list_filters import CreatedListFilter
17
13
  from edc_model_admin.mixins import (
18
14
  ModelAdminInstitutionMixin,
19
15
  ModelAdminNextUrlRedirectMixin,
@@ -22,6 +18,10 @@ from edc_model_admin.mixins import (
22
18
  TemplatesModelAdminMixin,
23
19
  )
24
20
  from edc_sites.admin import SiteModelAdminMixin
21
+ from rangefilter.filters import DateRangeFilterBuilder
22
+
23
+ from edc_metadata import KEYED, REQUIRED
24
+ from edc_metadata.admin.list_filters import CreatedListFilter
25
25
 
26
26
 
27
27
  class MetadataModelAdminMixin(
@@ -49,6 +49,8 @@ class MetadataModelAdminMixin(
49
49
 
50
50
  change_search_field_name = "subject_identifier"
51
51
 
52
+ subject_dashboard_url_name = "subject_dashboard_url" # url_name
53
+
52
54
  fieldsets = (
53
55
  [
54
56
  None,
@@ -149,8 +151,6 @@ class MetadataModelAdminMixin(
149
151
  extra_context.update(show_cancel=True)
150
152
  return extra_context
151
153
 
152
- subject_dashboard_url_name = "subject_dashboard_url"
153
-
154
154
  def get_subject_dashboard_url(self, obj=None) -> str | None:
155
155
  opts = {}
156
156
  if obj:
@@ -195,9 +195,11 @@ class MetadataModelAdminMixin(
195
195
  )
196
196
  return obj.get_entry_status_display()
197
197
 
198
- def get_view_on_site_url(self, obj=None):
198
+ def get_view_on_site_url(self, obj=None) -> None | str:
199
+ url = None
199
200
  if obj is None or not self.view_on_site:
200
- return None
201
+ url = None
201
202
  if hasattr(obj, "get_absolute_url"):
202
203
  url = reverse(self.changelist_url)
203
- return f"{url}?q={obj.subject_identifier}"
204
+ url = f"{url}?q={obj.subject_identifier}"
205
+ return url
@@ -12,7 +12,7 @@ style = color_style()
12
12
  class Command(BaseCommand):
13
13
  help = "Update metadata and re-run metadatarules"
14
14
 
15
- def handle(self, *args, **options) -> None:
15
+ def handle(self, *args, **options) -> None: # noqa: ARG002
16
16
  metadata_refresher = MetadataRefresher(verbose=True)
17
17
  sys.stdout.write("Deleting all CrfMetadata... \r")
18
18
  CrfMetadata.objects.all().delete()
@@ -12,7 +12,7 @@ style = color_style()
12
12
  class Command(BaseCommand):
13
13
  help = "Update metadata for changed visit_schedule/schedule names"
14
14
  pattern = "^[0-9a-z_]+$"
15
- fieldnames = ["visit_schedule_name", "schedule_name"]
15
+ fieldnames = ("visit_schedule_name", "schedule_name")
16
16
 
17
17
  def add_arguments(self, parser):
18
18
  parser.add_argument(
@@ -43,15 +43,15 @@ class Command(BaseCommand):
43
43
  help="Do a dry run. (Default: True)",
44
44
  )
45
45
 
46
- def handle(self, *args, **options):
47
- dry_run = False if options.get("dry_run", "") == "False" else True
46
+ def handle(self, *args, **options): # noqa: ARG002
47
+ dry_run = options.get("dry_run", "") != "False"
48
48
 
49
49
  try:
50
50
  UpdateMetadataOnScheduleChange(
51
- fieldname=options.get("field"),
52
- new_value=options.get("new_value"),
53
- old_value=options.get("old_value"),
51
+ field=options.get("field"),
52
+ new_name=options.get("new_value"),
53
+ old_name=options.get("old_value"),
54
54
  dry_run=dry_run,
55
55
  )
56
56
  except UpdateMetadataError as e:
57
- raise CommandError(e)
57
+ raise CommandError(e) from e
@@ -15,7 +15,7 @@ from ...models import CrfMetadata, RequisitionMetadata
15
15
  class Command(BaseCommand):
16
16
  help = "Performs a `get_model` for each target models referenced"
17
17
 
18
- def handle(self, *args, **options):
18
+ def handle(self, *args, **options): # noqa: ARG002
19
19
  grouping = (
20
20
  RequisitionMetadata.objects.distinct()
21
21
  .values("model")
@@ -6,5 +6,5 @@ from ...metadata_rules import site_metadata_rules
6
6
  class Command(BaseCommand):
7
7
  help = "Performs a `get_model` for each target models referenced"
8
8
 
9
- def handle(self, *args, **options):
9
+ def handle(self, *args, **options): # noqa: ARG002
10
10
  site_metadata_rules.validate()
@@ -47,7 +47,8 @@ class MetadataValidator:
47
47
  if not model_cls_registered_with_admin_site(source_model_cls):
48
48
  warn(
49
49
  "Model class not registered with Admin. "
50
- f"Deleting related metadata. Got {source_model_cls}."
50
+ f"Deleting related metadata. Got {source_model_cls}.",
51
+ stacklevel=2,
51
52
  )
52
53
  self.metadata_obj.delete()
53
54
  self.metadata_obj = None
@@ -75,10 +76,7 @@ class MetadataValidator:
75
76
  @staticmethod
76
77
  def model_cls_registered_with_admin_site(model_cls: Any) -> bool:
77
78
  """Returns True if model cls is registered in Admin."""
78
- for admin_site in all_sites:
79
- if model_cls in admin_site._registry:
80
- return True
81
- return False
79
+ return any(model_cls in admin_site._registry for admin_site in all_sites)
82
80
 
83
81
 
84
82
  class MetadataGetter:
@@ -25,7 +25,7 @@ class MetadataHandlerError(Exception):
25
25
  pass
26
26
 
27
27
 
28
- class MetadataObjectDoesNotExist(Exception):
28
+ class MetadataObjectDoesNotExist(Exception): # noqa: N818
29
29
  pass
30
30
 
31
31
 
@@ -73,15 +73,15 @@ class MetadataHandler:
73
73
  """Returns a new metadata model instance for this CRF."""
74
74
  metadata_obj = None
75
75
  try:
76
- crf = [
76
+ crf = next(
77
77
  f for f in self.creator.related_visit.visit.all_crfs if f.model == self.model
78
- ][0]
79
- except IndexError as e:
78
+ )
79
+ except StopIteration as e:
80
80
  if self.related_visit.reason != MISSED_VISIT:
81
81
  raise MetadataHandlerError(
82
82
  "Create failed. Model not found. Not in visit.all_crfs. "
83
83
  f"Model {self.model}. Got {e}"
84
- )
84
+ ) from e
85
85
  else:
86
86
  metadata_obj = self.creator.create_crf(crf)
87
87
  return metadata_obj
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
22
22
  class SourceModelMetadataMixin:
23
23
  """Mixin class for Metadata and MetadataUpdater class."""
24
24
 
25
- def __init__(self, source_model: str, related_visit: RelatedVisitModel = None):
25
+ def __init__(self, source_model: str, related_visit: RelatedVisitModel):
26
26
  self._source_model_obj = None
27
27
  self._source_model = source_model
28
28
  self.related_visit = related_visit
@@ -44,7 +44,7 @@ class MetadataRefresher:
44
44
  def source_models(self) -> list[str]:
45
45
  if not self._source_models:
46
46
  self._source_models = []
47
- for app_label, rule_groups_list in site_metadata_rules.rule_groups.items():
47
+ for rule_groups_list in site_metadata_rules.rule_groups.values():
48
48
  for rule_groups in rule_groups_list:
49
49
  if (
50
50
  rule_groups._meta.source_model
@@ -19,7 +19,7 @@ class CrfRuleModelConflict(Exception): # noqa: N818
19
19
 
20
20
 
21
21
  class CrfRule(Rule):
22
- def __init__(self, target_models: list[str] = None, **kwargs) -> None:
22
+ def __init__(self, target_models: list[str], **kwargs) -> None:
23
23
  super().__init__(**kwargs)
24
24
  self.metadata_category = CRF
25
25
  self.target_models = target_models
@@ -29,9 +29,9 @@ class Logic:
29
29
 
30
30
  def __init__(
31
31
  self,
32
- predicate: P | PF | Callable | None = None,
33
- consequence: str | None = None,
34
- alternative: str | None = None,
32
+ predicate: P | PF | Callable,
33
+ consequence: str,
34
+ alternative: str,
35
35
  comment: str | None = None,
36
36
  ) -> None:
37
37
  if not callable(predicate):
@@ -36,11 +36,9 @@ class PersistantSingletonMixin:
36
36
  )
37
37
  except ObjectDoesNotExist:
38
38
  obj = None
39
- required = (
40
- True
41
- if visit == self.get_last_attended_scheduled_visit(visit)
39
+ required = bool(
40
+ visit == self.get_last_attended_scheduled_visit(visit)
42
41
  and visit.visit_code not in exclude_visit_codes
43
- else False
44
42
  )
45
43
  except MultipleObjectsReturned:
46
44
  # necessary if the collection schedule changes and a singleton form
@@ -33,7 +33,7 @@ class RequisitionRuleGroupMetaOptions(RuleGroupMetaOptions):
33
33
  super().__init__(group_name, attrs)
34
34
  self.requisition_model = self.options.get("requisition_model")
35
35
  if self.requisition_model:
36
- if len(self.requisition_model.split(".")) != 2:
36
+ if len(self.requisition_model.split(".")) != 2: # noqa: PLR2004
37
37
  self.requisition_model = f"{self.app_label}.{self.requisition_model}"
38
38
  self.options.update(requisition_model=self.requisition_model)
39
39
  self.options.update(target_models=[self.requisition_model])
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import contextlib
4
+ from collections.abc import Callable
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  from edc_appointment.constants import MISSED_APPT
@@ -28,9 +29,9 @@ class Rule:
28
29
 
29
30
  def __init__(
30
31
  self,
31
- predicate: P | PF | callable | str = None,
32
- consequence: str = None,
33
- alternative: str = None,
32
+ predicate: P | PF | Callable | str,
33
+ consequence: str,
34
+ alternative: str,
34
35
  ) -> None:
35
36
  self.predicate = predicate
36
37
  self.consequence = consequence
@@ -11,7 +11,7 @@ class RuleGroupError(Exception):
11
11
  pass
12
12
 
13
13
 
14
- class TargetModelConflict(Exception):
14
+ class TargetModelConflict(Exception): # noqa: N818
15
15
  pass
16
16
 
17
17
 
@@ -61,7 +61,7 @@ class RuleGroup:
61
61
  )
62
62
 
63
63
  @classmethod
64
- def _lookup_model(cls, model: str = None, category: str = None) -> Any:
64
+ def _lookup_model(cls, model: str, category: str) -> Any:
65
65
  sys.stdout.write(f" ( ) {model}\r")
66
66
  model_cls = None
67
67
  try:
@@ -42,13 +42,13 @@ class RuleGroupMetaOptions:
42
42
  # source model
43
43
  self.source_model = self.options.get("source_model")
44
44
  if self.source_model:
45
- if len(self.source_model.split(".")) != 2:
45
+ if len(self.source_model.split(".")) != 2: # noqa: PLR2004
46
46
  self.source_model = f"{self.app_label}.{self.source_model}"
47
47
  self.options.update(source_model=self.source_model)
48
48
  # related visit model
49
49
  self.related_visit_model = self.options.get("related_visit_model")
50
50
  if self.related_visit_model:
51
- if len(self.related_visit_model.split(".")) != 2:
51
+ if len(self.related_visit_model.split(".")) != 2: # noqa: PLR2004
52
52
  raise RuleGroupMetaError(
53
53
  "Invalid _meta attr. Expected _meta.related_visit_model to be in "
54
54
  f"label_lower format. Got '{self.related_visit_model}'. See {group_name}."
@@ -51,7 +51,7 @@ class RuleGroupMetaclass(type):
51
51
  return super().__new__(mcs, name, bases, attrs)
52
52
 
53
53
  @classmethod
54
- def __get_rules(mcs, name: str, attrs: dict, meta: Any) -> tuple:
54
+ def __get_rules(mcs, name: str, attrs: dict, meta: Any) -> tuple: # noqa: N804
55
55
  """Returns a list of rules after updating each rule's attrs
56
56
  with values from Meta.
57
57
 
@@ -59,28 +59,27 @@ class RuleGroupMetaclass(type):
59
59
  """
60
60
  rules = []
61
61
  for key, value in attrs.items():
62
- if not key.startswith("_"):
63
- if isinstance(value, Rule):
64
- rule = value
65
- rule.name = key
66
- rule.group = name
67
- if isinstance(rule.predicate, (str,)):
68
- predicates = getattr(meta, "predicates", None)
69
- if not predicates:
70
- raise RuleGroupError(
71
- "RuleGroup Meta attr `predicates` may not be `None` if a "
72
- "rule.predicate in the RuleGroup is a string. "
73
- f"See {attrs.get('__qualname__')}."
74
- )
75
- rule.predicate = getattr(meta.predicates, rule.predicate)
76
- for k, v in meta.options.items():
77
- setattr(rule, k, v)
78
- rule.target_models = mcs.__get_target_models(rule, meta)
79
- rules.append(rule)
62
+ if not key.startswith("_") and isinstance(value, Rule):
63
+ rule = value
64
+ rule.name = key
65
+ rule.group = name
66
+ if isinstance(rule.predicate, (str,)):
67
+ predicates = getattr(meta, "predicates", None)
68
+ if not predicates:
69
+ raise RuleGroupError(
70
+ "RuleGroup Meta attr `predicates` may not be `None` if a "
71
+ "rule.predicate in the RuleGroup is a string. "
72
+ f"See {attrs.get('__qualname__')}."
73
+ )
74
+ rule.predicate = getattr(meta.predicates, rule.predicate)
75
+ for k, v in meta.options.items():
76
+ setattr(rule, k, v)
77
+ rule.target_models = mcs.__get_target_models(rule, meta)
78
+ rules.append(rule)
80
79
  return tuple(rules)
81
80
 
82
81
  @classmethod
83
- def __get_target_models(mcs, rule: Any, meta: Any) -> Any:
82
+ def __get_target_models(mcs, rule: Any, meta: Any) -> Any: # noqa: N804
84
83
  """Returns target models as a list of label_lowers.
85
84
 
86
85
  Target models are the models whose metadata is acted upon.
@@ -90,7 +89,7 @@ class RuleGroupMetaclass(type):
90
89
  """
91
90
  target_models = []
92
91
  for target_model in rule.target_models:
93
- if len(target_model.split(".")) != 2:
94
- target_model = f"{meta.app_label}.{target_model}"
92
+ if len(target_model.split(".")) != 2: # noqa: PLR2004
93
+ target_model = f"{meta.app_label}.{target_model}" # noqa: PLW2901
95
94
  target_models.append(target_model)
96
95
  return target_models
@@ -9,7 +9,7 @@ from django.utils.module_loading import import_module, module_has_submodule
9
9
  style = color_style()
10
10
 
11
11
 
12
- class SiteMetadataRulesAlreadyRegistered(Exception):
12
+ class SiteMetadataRulesAlreadyRegistered(Exception): # noqa: N818
13
13
  pass
14
14
 
15
15
 
@@ -31,8 +31,9 @@ class MetadataUpdater(SourceModelMetadataMixin):
31
31
 
32
32
  def __init__(
33
33
  self,
34
- related_visit: RelatedVisitModel = None,
35
- source_model: str = None,
34
+ *,
35
+ related_visit: RelatedVisitModel,
36
+ source_model: str,
36
37
  allow_create: bool | None = None,
37
38
  ):
38
39
  super().__init__(source_model, related_visit)
@@ -46,7 +47,7 @@ class MetadataUpdater(SourceModelMetadataMixin):
46
47
  f"source_model={self.source_model})"
47
48
  )
48
49
 
49
- def get_and_update(self, entry_status: str = None) -> CrfMetadata | RequisitionMetadata:
50
+ def get_and_update(self, entry_status: str) -> CrfMetadata | RequisitionMetadata:
50
51
  metadata_obj = self.metadata_handler.metadata_obj
51
52
  if entry_status != KEYED and self.source_model_obj_exists:
52
53
  entry_status = KEYED