clinicedc 2.0.38__py3-none-any.whl → 2.0.40__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 (157) hide show
  1. {clinicedc-2.0.38.dist-info → clinicedc-2.0.40.dist-info}/METADATA +3 -12
  2. {clinicedc-2.0.38.dist-info → clinicedc-2.0.40.dist-info}/RECORD +146 -153
  3. {clinicedc-2.0.38.dist-info → clinicedc-2.0.40.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/templates/edc_adverse_event/tmg/tmg_ae_listboard_result.html +27 -23
  8. edc_adverse_event/templatetags/edc_adverse_event_extras.py +21 -34
  9. edc_adverse_event/urls.py +14 -6
  10. edc_adverse_event/view_mixins/ae/ae_listboard_view_mixin.py +6 -8
  11. edc_adverse_event/view_mixins/ae/death_report_listboard_view_mixin.py +2 -4
  12. edc_adverse_event/view_mixins/tmg/tmg_ae_listboard_view_mixin.py +9 -7
  13. edc_adverse_event/views/home_view.py +1 -2
  14. edc_adverse_event/views/tmg/death_listboard_view.py +8 -6
  15. edc_adverse_event/views/tmg/home_view.py +4 -3
  16. edc_adverse_event/views/tmg/summary_listboard_view.py +4 -4
  17. edc_appointment/utils.py +3 -6
  18. edc_appointment/views/unscheduled_appointment_view.py +1 -1
  19. edc_consent/form_validators/consent_definition_form_validator_mixin.py +5 -2
  20. edc_consent/model_mixins/consent_version_model_mixin.py +1 -1
  21. edc_consent/navbars.py +2 -1
  22. edc_crf/model_mixins/crf_model_mixin.py +5 -1
  23. edc_crf/model_mixins/crf_no_manager_model_mixin.py +2 -2
  24. edc_crf/model_mixins/singleton_crf_model_mixin.py +1 -1
  25. edc_dashboard/middleware.py +10 -16
  26. edc_dashboard/middleware_mixins.py +10 -0
  27. edc_dashboard/navbars.py +1 -1
  28. edc_dashboard/url_config.py +50 -31
  29. edc_dashboard/url_names.py +23 -17
  30. edc_dashboard/utils.py +4 -4
  31. edc_dashboard/view_mixins/template_request_context_mixin.py +5 -8
  32. edc_dashboard/view_mixins/url_request_context_mixin.py +38 -26
  33. edc_dashboard/views/administration_view.py +2 -2
  34. edc_dashboard/views/dashboard_view.py +5 -10
  35. edc_data_manager/handlers/handlers.py +17 -5
  36. edc_data_manager/models/query_rule.py +7 -7
  37. edc_data_manager/navbar_item.py +1 -1
  38. edc_data_manager/rule/query_rule_wrapper.py +1 -1
  39. edc_data_manager/rule/rule_runner.py +6 -6
  40. edc_device/navbars.py +1 -1
  41. edc_export/navbars.py +2 -2
  42. edc_glucose/model_mixin_factories/fasting_model_mixin_factory.py +1 -1
  43. edc_identifier/identifier.py +6 -9
  44. edc_lab_dashboard/dashboard_urls.py +7 -5
  45. edc_lab_dashboard/middleware.py +10 -17
  46. edc_lab_dashboard/navbars.py +9 -9
  47. edc_lab_dashboard/templates/edc_lab_dashboard/listboard/tags/status_column.html +7 -0
  48. edc_lab_dashboard/urls.py +2 -5
  49. edc_lab_dashboard/view_mixins/form_action_view_mixin.py +1 -2
  50. edc_lab_dashboard/views/action_views/action_view.py +6 -6
  51. edc_lab_dashboard/views/action_views/aliquot_view.py +1 -1
  52. edc_lab_dashboard/views/action_views/manage_box_item_view.py +2 -3
  53. edc_lab_dashboard/views/action_views/manage_manifest_view.py +1 -1
  54. edc_lab_dashboard/views/action_views/manifest_view.py +2 -2
  55. edc_lab_dashboard/views/action_views/pack_view.py +2 -2
  56. edc_lab_dashboard/views/action_views/process_view.py +1 -1
  57. edc_lab_dashboard/views/action_views/receive_view.py +1 -1
  58. edc_lab_dashboard/views/action_views/requisition_view.py +1 -1
  59. edc_lab_dashboard/views/action_views/verify_box_item_view.py +1 -1
  60. edc_lab_dashboard/views/listboard_views/manage_box_listboard_view.py +4 -5
  61. edc_lab_dashboard/views/listboard_views/manifest_listboard_view.py +5 -6
  62. edc_lab_dashboard/views/listboard_views/process_listboard_view.py +4 -5
  63. edc_lab_dashboard/views/listboard_views/receive_listboard_view.py +5 -6
  64. edc_lab_dashboard/views/listboard_views/verify_box_listboard_view.py +5 -6
  65. edc_label/navbars.py +1 -1
  66. edc_list_data/admin.py +3 -3
  67. edc_list_data/load_model_data.py +1 -1
  68. edc_list_data/management/commands/load_list_data.py +2 -2
  69. edc_list_data/site_list_data.py +4 -4
  70. edc_listboard/middleware.py +9 -8
  71. edc_listboard/templates/edc_listboard/listboard.html +1 -1
  72. edc_listboard/view_mixins/listboard_filter_view_mixin.py +1 -1
  73. edc_listboard/view_mixins/search_form_view_mixin.py +1 -1
  74. edc_listboard/views/listboard_view.py +16 -25
  75. edc_listboard/views/screen/screening_listboard_view.py +2 -2
  76. edc_listboard/views/subject/subject_listboard_view.py +2 -2
  77. edc_locator/forms/subject_locator_form_validator.py +2 -2
  78. edc_ltfu/action_items.py +1 -2
  79. edc_ltfu/forms/ltfu_form_validator_mixin.py +3 -3
  80. edc_ltfu/modeladmin_mixin.py +1 -1
  81. edc_ltfu/modelform_mixins.py +2 -2
  82. edc_metadata/admin/modeladmin_mixins.py +11 -9
  83. edc_metadata/management/commands/update_metadata.py +1 -1
  84. edc_metadata/management/commands/update_metadata_schedule_names.py +7 -7
  85. edc_metadata/management/commands/validate_entry_status.py +1 -1
  86. edc_metadata/management/commands/validate_rule_groups.py +1 -1
  87. edc_metadata/metadata/metadata_getter.py +3 -5
  88. edc_metadata/metadata_handler.py +5 -5
  89. edc_metadata/metadata_mixins/source_model_metadata_mixin.py +1 -1
  90. edc_metadata/metadata_refresher.py +1 -1
  91. edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
  92. edc_metadata/metadata_rules/logic.py +3 -3
  93. edc_metadata/metadata_rules/persistant_singleton_mixin.py +2 -4
  94. edc_metadata/metadata_rules/requisition/requisition_rule_group.py +1 -1
  95. edc_metadata/metadata_rules/rule.py +4 -3
  96. edc_metadata/metadata_rules/rule_group.py +2 -2
  97. edc_metadata/metadata_rules/rule_group_meta_options.py +2 -2
  98. edc_metadata/metadata_rules/rule_group_metaclass.py +21 -22
  99. edc_metadata/metadata_rules/site.py +1 -1
  100. edc_metadata/metadata_updater.py +4 -3
  101. edc_metadata/model_mixins/creates/creates_metadata_model_mixin.py +3 -5
  102. edc_metadata/model_mixins/updates/updates_metadata_model_mixin.py +1 -1
  103. edc_metadata/next_form_getter.py +15 -19
  104. edc_metadata/offline_models.py +1 -1
  105. edc_metadata/requisition/requisition_metadata_handler.py +5 -5
  106. edc_metadata/update_metadata_on_schedule_change.py +2 -4
  107. edc_metadata/utils.py +1 -1
  108. edc_model/models/signals.py +7 -2
  109. edc_model_admin/mixins/model_admin_redirect_on_delete_mixin.py +4 -3
  110. edc_navbar/apps.py +0 -2
  111. edc_navbar/navbar.py +1 -1
  112. edc_navbar/navbar_item.py +29 -16
  113. edc_navbar/navbars.py +6 -19
  114. edc_navbar/site_navbars.py +6 -7
  115. edc_navbar/system_checks.py +3 -10
  116. edc_navbar/utils.py +14 -0
  117. edc_navbar/view_mixin.py +6 -9
  118. edc_pharmacy/navbars.py +1 -1
  119. edc_pharmacy/views/confirm_stock_from_queryset_view.py +3 -3
  120. edc_protocol/middleware.py +9 -13
  121. edc_protocol/navbars.py +1 -1
  122. edc_refusal/forms.py +1 -3
  123. edc_reportable/utils/convert_units.py +1 -1
  124. edc_review_dashboard/middleware.py +6 -3
  125. edc_review_dashboard/navbars.py +1 -2
  126. edc_review_dashboard/urls.py +3 -2
  127. edc_review_dashboard/views/subject_review_listboard_view.py +4 -2
  128. edc_subject_dashboard/dashboard_templates.py +1 -3
  129. edc_subject_dashboard/dashboard_urls.py +8 -0
  130. edc_subject_dashboard/middleware.py +10 -7
  131. edc_subject_dashboard/templates/edc_subject_dashboard/buttons/refresh_appointments_button.html +1 -1
  132. edc_subject_dashboard/templates/edc_subject_dashboard/dashboard.html +1 -1
  133. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +3 -1
  134. edc_subject_dashboard/urls.py +13 -4
  135. edc_subject_dashboard/views/base_requisition_view.py +2 -1
  136. edc_subject_dashboard/views/subject_dashboard_view.py +1 -2
  137. edc_timepoint/__init__.py +0 -2
  138. edc_timepoint/model_mixins.py +1 -2
  139. edc_timepoint/utils.py +1 -1
  140. edc_timepoint/visit_timepoint_lookup.py +6 -0
  141. edc_visit_schedule/admin/subject_schedule_history_admin.py +1 -2
  142. edc_visit_schedule/navbars.py +3 -4
  143. edc_visit_schedule/visit/visit.py +15 -0
  144. edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +5 -0
  145. edc_visit_tracking/models/subject_visit.py +5 -0
  146. edc_lab_dashboard/model_wrappers/__init__.py +0 -8
  147. edc_lab_dashboard/model_wrappers/aliquot_model_wrapper.py +0 -31
  148. edc_lab_dashboard/model_wrappers/base_box_item_model_wrapper.py +0 -21
  149. edc_lab_dashboard/model_wrappers/box_model_wrapper.py +0 -12
  150. edc_lab_dashboard/model_wrappers/manage_box_item_model_wrapper.py +0 -6
  151. edc_lab_dashboard/model_wrappers/manifest_item_model_wrapper.py +0 -21
  152. edc_lab_dashboard/model_wrappers/manifest_model_wrapper.py +0 -11
  153. edc_lab_dashboard/model_wrappers/requisition_model_wrapper.py +0 -25
  154. edc_lab_dashboard/model_wrappers/result_model_wrapper.py +0 -8
  155. edc_lab_dashboard/model_wrappers/verify_box_model_wrapper.py +0 -10
  156. edc_navbar/get_default_navbar.py +0 -9
  157. {clinicedc-2.0.38.dist-info → clinicedc-2.0.40.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,6 @@ from django.apps import apps as django_apps
6
6
  from django.urls import reverse
7
7
  from django.utils.html import format_html
8
8
  from django.utils.safestring import mark_safe
9
-
10
9
  from edc_constants.constants import YES
11
10
  from edc_dashboard.url_names import url_names
12
11
 
@@ -20,15 +19,15 @@ app_config = django_apps.get_app_config("edc_lab_dashboard")
20
19
 
21
20
  class ReceiveListboardView(RequisitionListboardView):
22
21
  action_name = "receive"
23
- form_action_url = "receive_form_action_url"
22
+ form_action_url = "receive_form_action_url" # url_name
24
23
  listboard_template = "receive_listboard_template"
25
- listboard_url = "receive_listboard_url"
24
+ listboard_url = "receive_listboard_url" # url_name
26
25
  listboard_view_permission_codename = "edc_lab_dashboard.view_lab_receive_listboard"
27
26
  listboard_view_only_my_permission_codename = None
28
27
  navbar_selected_item = "receive"
29
- process_listboard_url = "process_listboard_url"
28
+ process_listboard_url = "process_listboard_url" # url_name
30
29
  show_all = True
31
- search_form_url = "receive_listboard_url"
30
+ search_form_url = "receive_listboard_url" # url_name
32
31
 
33
32
  def get_queryset_filter_options(self, request, *args, **kwargs) -> tuple[Q, dict]:
34
33
  q_object, options = super().get_queryset_filter_options(request, *args, **kwargs)
@@ -36,7 +35,7 @@ class ReceiveListboardView(RequisitionListboardView):
36
35
  return q_object, options
37
36
 
38
37
  def get_empty_queryset_message(self) -> str:
39
- href = reverse(url_names.get("process_listboard_url"))
38
+ href = reverse(url_names.get(self.process_listboard_url))
40
39
  return format_html(
41
40
  "All specimens have been received. Continue to "
42
41
  '<a href="{}" class="alert-link">processing</a>',
@@ -2,7 +2,6 @@ from copy import copy
2
2
  from typing import Any
3
3
 
4
4
  from django.urls import reverse
5
-
6
5
  from edc_dashboard.url_names import url_names
7
6
  from edc_lab.constants import SHIPPED
8
7
 
@@ -11,13 +10,13 @@ from .base_box_item_listboard_view import BaseBoxItemListboardView
11
10
 
12
11
  class VerifyBoxListboardView(BaseBoxItemListboardView):
13
12
  action_name = "verify"
14
- form_action_url = "verify_box_item_form_action_url"
13
+ form_action_url = "verify_box_item_form_action_url" # url_name
15
14
  listboard_template = "verify_box_listboard_template"
16
- listboard_url = "verify_box_listboard_url"
15
+ listboard_url = "verify_box_listboard_url" # url_name
17
16
  navbar_selected_item = "pack"
18
- search_form_url = "verify_box_listboard_url"
19
- manage_box_listboard_url = "manage_box_listboard_url"
20
- verify_box_listboard_url = "verify_box_listboard_url"
17
+ search_form_url = "verify_box_listboard_url" # url_name
18
+ manage_box_listboard_url = "manage_box_listboard_url" # url_name
19
+ verify_box_listboard_url = "verify_box_listboard_url" # url_name
21
20
 
22
21
  def get_context_data(self, **kwargs) -> dict[str, Any]:
23
22
  kwargs.update(
edc_label/navbars.py CHANGED
@@ -7,7 +7,7 @@ label.register(
7
7
  name="label",
8
8
  label="Label",
9
9
  fa_icon="fa-film",
10
- url_name="edc_label:home_url",
10
+ url_with_namespace="edc_label:home_url",
11
11
  codename="edc_navbar.nav_label",
12
12
  )
13
13
  )
edc_list_data/admin.py CHANGED
@@ -4,8 +4,8 @@ from edc_model_admin.mixins import TemplatesModelAdminMixin
4
4
 
5
5
 
6
6
  class ListModelAdminMixin(TemplatesModelAdminMixin, admin.ModelAdmin):
7
- ordering: tuple[str, ...] = ("display_index", "display_name")
7
+ ordering = ("display_index", "display_name")
8
8
 
9
- list_display: tuple[str, ...] = ("display_name", "name", "display_index")
9
+ list_display = ("display_name", "name", "display_index")
10
10
 
11
- search_fields: tuple[str, ...] = ("display_name", "name")
11
+ search_fields = ("display_name", "name")
@@ -27,7 +27,7 @@ def load_model_data(model_data: dict, apps: AppConfig | None = None) -> int:
27
27
  n = 0
28
28
  for model_name, options in model_data.items():
29
29
  try:
30
- model_name, unique_field = model_name
30
+ model_name, unique_field = model_name # noqa: PLW2901
31
31
  except ValueError:
32
32
  unique_field = None
33
33
  model = apps.get_model(model_name)
@@ -7,8 +7,8 @@ class Command(BaseCommand):
7
7
  help = "Populates list data and other static model data from list_data.py"
8
8
  module_name = "list_data"
9
9
 
10
- def handle(self, *args, **options):
10
+ def handle(self, *args, **options): # noqa: ARG002
11
11
  try:
12
12
  site_list_data.autodiscover()
13
13
  except SiteListDataError as e:
14
- raise CommandError(e)
14
+ raise CommandError(e) from e
@@ -13,11 +13,11 @@ from .load_list_data import LoadListDataError
13
13
  from .preload_data import PreloadData
14
14
 
15
15
 
16
- class AlreadyRegistered(Exception):
16
+ class AlreadyRegistered(Exception): # noqa: N818
17
17
  pass
18
18
 
19
19
 
20
- class AlreadyLoaded(Exception):
20
+ class AlreadyLoaded(Exception): # noqa: N818
21
21
  pass
22
22
 
23
23
 
@@ -35,7 +35,7 @@ class SiteListData:
35
35
  Called in AppConfig or by management command.
36
36
  """
37
37
 
38
- default_module_prefixes = ["edc_"]
38
+ default_module_prefixes = ("edc_",)
39
39
  default_module_name = "list_data"
40
40
 
41
41
  def __init__(self, module_name=None):
@@ -87,7 +87,7 @@ class SiteListData:
87
87
  * only edc_* modules can provide defaults
88
88
  """
89
89
  models = []
90
- for label_lower, data in opts.get(self.module_name).items():
90
+ for label_lower in opts.get(self.module_name):
91
91
  default_module_name = self._get_default_module_name(module, label_lower)
92
92
  if label_lower not in self.models:
93
93
  models.append(label_lower)
@@ -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])