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
@@ -23,7 +23,9 @@ if TYPE_CHECKING:
23
23
  class ConsentDefinitionFormValidatorMixin:
24
24
  @property
25
25
  def subject_consent(self):
26
- cdef = self.get_consent_definition()
26
+ cdef = self.get_consent_definition(
27
+ self.report_datetime, self.report_datetime_field_attr
28
+ )
27
29
  return cdef.model_cls.objects.get(subject_identifier=self.subject_identifier)
28
30
 
29
31
  def get_consent_datetime_or_raise(
@@ -71,8 +73,9 @@ class ConsentDefinitionFormValidatorMixin:
71
73
  self,
72
74
  report_datetime: datetime,
73
75
  fldname: str = None,
74
- error_code: str = None,
76
+ error_code: str | None = None,
75
77
  ) -> ConsentDefinition:
78
+ error_code = error_code or INVALID_ERROR
76
79
  # get the consent definition (must be from this schedule)
77
80
  schedule = getattr(self, "related_visit", self.instance).schedule
78
81
 
@@ -24,7 +24,7 @@ class ConsentVersionModelMixin(models.Model):
24
24
  update_versions = models.BooleanField(default=False)
25
25
 
26
26
  consent_definition_name = models.CharField(
27
- verbose_name="Consent definition", max_length=50, null=True, editable=False
27
+ verbose_name="Consent definition", max_length=50, default="", editable=False
28
28
  )
29
29
 
30
30
  def __str__(self):
edc_consent/navbars.py CHANGED
@@ -7,7 +7,8 @@ navbar.register(
7
7
  name="consent",
8
8
  label="Consent",
9
9
  fa_icon="fa-user-circle",
10
- url_name="edc_consent:home_url",
10
+ url_names_key="consent_home",
11
+ url_with_namespace="edc_consent:home_url",
11
12
  codename="edc_consent.nav_consent",
12
13
  )
13
14
  )
@@ -2,12 +2,16 @@ from django.contrib.sites.models import Site
2
2
 
3
3
  from edc_model.models import HistoricalRecords
4
4
  from edc_sites.model_mixins import SiteModelMixin
5
+ from edc_timepoint.model_mixins import TimepointLookupModelMixin
6
+ from edc_timepoint.timepoint_lookup import TimepointLookup
5
7
  from edc_visit_tracking.managers import CrfCurrentSiteManager, CrfModelManager
6
8
 
7
9
  from .crf_no_manager_model_mixin import CrfNoManagerModelMixin
8
10
 
9
11
 
10
- class CrfModelMixin(SiteModelMixin, CrfNoManagerModelMixin):
12
+ class CrfModelMixin(SiteModelMixin, TimepointLookupModelMixin, CrfNoManagerModelMixin):
13
+ timepoint_lookup_cls = TimepointLookup
14
+
11
15
  objects = CrfModelManager()
12
16
  on_site = CrfCurrentSiteManager()
13
17
  history = HistoricalRecords(inherit=True)
@@ -23,11 +23,11 @@ class CrfNoManagerModelMixin(
23
23
  def natural_key(self) -> tuple:
24
24
  return self.related_visit.natural_key()
25
25
 
26
- natural_key.dependencies = [
26
+ natural_key.dependencies = (
27
27
  settings.SUBJECT_VISIT_MODEL,
28
28
  "sites.Site",
29
29
  "edc_appointment.appointment",
30
- ]
30
+ )
31
31
 
32
32
  class Meta:
33
33
  abstract = True
@@ -1,6 +1,7 @@
1
1
  from django.db import models
2
2
 
3
3
 
4
+
4
5
  class SingletonCrfModelMixin(models.Model):
5
6
  """Enforces one record per subject.
6
7
 
@@ -12,7 +13,6 @@ class SingletonCrfModelMixin(models.Model):
12
13
  max_length=50,
13
14
  unique=True,
14
15
  help_text="auto updated for unique constraint",
15
- null=True,
16
16
  editable=False,
17
17
  )
18
18
 
@@ -1,33 +1,27 @@
1
1
  from django.conf import settings
2
2
 
3
3
  from .dashboard_templates import dashboard_templates
4
+ from .middleware_mixins import EdcTemplateMiddlewareMixin
4
5
  from .url_names import url_names
5
6
 
6
7
 
7
- class DashboardMiddleware:
8
+ class DashboardMiddleware(EdcTemplateMiddlewareMixin):
8
9
  def __init__(self, get_response):
9
10
  self.get_response = get_response
10
11
 
11
12
  def __call__(self, request):
12
- try:
13
- request.url_name_data
14
- except AttributeError:
15
- request.url_name_data = url_names.registry
16
- try:
17
- request.template_data
18
- except AttributeError:
19
- request.template_data = {}
20
- response = self.get_response(request)
21
- return response
13
+ self.check_for_required_request_attrs(request)
14
+ return self.get_response(request)
22
15
 
23
16
  def process_view(self, request, *args):
24
17
  """Adds/Updates references to urls and templates."""
25
- template_data = dashboard_templates
26
- try:
27
- template_data.update(settings.DASHBOARD_BASE_TEMPLATES)
28
- except AttributeError:
29
- pass
18
+ request.url_name_data.update(**url_names.registry)
19
+ template_data = getattr(settings, "DASHBOARD_BASE_TEMPLATES", {})
20
+ template_data.update(**dashboard_templates)
30
21
  request.template_data.update(**template_data)
31
22
 
32
23
  def process_template_response(self, request, response):
24
+ if getattr(response, "context_data", None):
25
+ response.context_data.update(**request.url_name_data)
26
+ response.context_data.update(**request.template_data)
33
27
  return response
@@ -0,0 +1,10 @@
1
+ class EdcTemplateMiddlewareMixin:
2
+ def check_for_required_request_attrs(self, request):
3
+ try:
4
+ request.url_name_data # noqa: B018
5
+ except AttributeError:
6
+ request.url_name_data = {}
7
+ try:
8
+ request.template_data # noqa: B018
9
+ except AttributeError:
10
+ request.template_data = {}
edc_dashboard/navbars.py CHANGED
@@ -7,7 +7,7 @@ navbar.register(
7
7
  label="Edc Dashboard",
8
8
  fa_icon="fa-cogs",
9
9
  codename="edc_navbar.nav_edc_dashboard",
10
- url_name="edc_dashboard:home_url",
10
+ url_with_namespace="edc_dashboard:home_url",
11
11
  )
12
12
  )
13
13
 
@@ -17,25 +17,44 @@ if TYPE_CHECKING:
17
17
  class View(UrlRequestContextMixin, BaseView): ...
18
18
 
19
19
 
20
+ class UrlConfigError(Exception):
21
+ pass
22
+
23
+
20
24
  class UrlConfig:
25
+ """A class to generate url_patterns for edc DashboardViews,
26
+ ListBoardViews and SubjectReviewDashboardView.
27
+
28
+ * registers the url_with_namespace to `url_names`
29
+ * The pretty url uses the `url_names_key` less the '_url' suffix
30
+ * the url pattern name is the same as the given `url_names_key`
31
+
32
+ """
33
+
21
34
  def __init__(
22
35
  self,
23
36
  *,
24
- url_name: str,
37
+ url_names_key: str,
25
38
  namespace: str,
26
39
  view_class: type[View | UrlRequestContextMixin],
27
- label: str,
28
40
  identifier_label: str,
29
41
  identifier_pattern: str,
30
42
  ):
43
+ if not url_names_key.endswith("_url"):
44
+ raise UrlConfigError(
45
+ f"Invalid `url_names_key`. Must end with '_url'. Got {url_names_key}."
46
+ )
47
+ self.url_pattern_name = url_names_key
48
+ self.url_pretty_label = url_names_key.replace("_url", "")
49
+ self.view_class = view_class
31
50
  self.identifier_label = identifier_label
32
51
  self.identifier_pattern = identifier_pattern
33
- self.label = label
34
- self.url_name = url_name
35
- self.view_class = view_class
36
52
 
37
- # register {urlname, namespace:urlname} with url_names
38
- url_names.register(url=self.url_name, namespace=namespace)
53
+ # register with url_names dictionary / registry
54
+ url_names.register(
55
+ key=url_names_key,
56
+ url_with_namespace=f"{namespace}:{self.url_pattern_name}",
57
+ )
39
58
 
40
59
  @property
41
60
  def dashboard_urls(self) -> list[URLPattern]:
@@ -49,13 +68,13 @@ class UrlConfig:
49
68
  r"(?P<visit_code>\w+)/"
50
69
  r"(?P<unscheduled>\w+)/".format(
51
70
  **dict(
52
- label=self.label,
71
+ label=self.url_pretty_label,
53
72
  identifier_label=self.identifier_label,
54
73
  identifier_pattern=self.identifier_pattern,
55
74
  )
56
75
  ),
57
76
  self.view_class.as_view(),
58
- name=self.url_name,
77
+ name=self.url_pattern_name,
59
78
  ),
60
79
  re_path(
61
80
  "{label}/"
@@ -64,13 +83,13 @@ class UrlConfig:
64
83
  r"(?P<schedule_name>\w+)/"
65
84
  r"(?P<visit_code>\w+)/".format(
66
85
  **dict(
67
- label=self.label,
86
+ label=self.url_pretty_label,
68
87
  identifier_label=self.identifier_label,
69
88
  identifier_pattern=self.identifier_pattern,
70
89
  )
71
90
  ),
72
91
  self.view_class.as_view(),
73
- name=self.url_name,
92
+ name=self.url_pattern_name,
74
93
  ),
75
94
  re_path(
76
95
  "{label}/"
@@ -79,14 +98,14 @@ class UrlConfig:
79
98
  r"(?P<scanning>\d)/"
80
99
  r"(?P<error>\d)/".format(
81
100
  **dict(
82
- label=self.label,
101
+ label=self.url_pretty_label,
83
102
  identifier_label=self.identifier_label,
84
103
  identifier_pattern=self.identifier_pattern,
85
104
  uuid_pattern=UUID_PATTERN.pattern,
86
105
  )
87
106
  ),
88
107
  self.view_class.as_view(),
89
- name=self.url_name,
108
+ name=self.url_pattern_name,
90
109
  ),
91
110
  re_path(
92
111
  "{label}/"
@@ -94,52 +113,52 @@ class UrlConfig:
94
113
  "(?P<appointment>{uuid_pattern})/"
95
114
  r"(?P<reason>\w+)/".format(
96
115
  **dict(
97
- label=self.label,
116
+ label=self.url_pretty_label,
98
117
  identifier_label=self.identifier_label,
99
118
  identifier_pattern=self.identifier_pattern,
100
119
  uuid_pattern=UUID_PATTERN.pattern,
101
120
  )
102
121
  ),
103
122
  self.view_class.as_view(),
104
- name=self.url_name,
123
+ name=self.url_pattern_name,
105
124
  ),
106
125
  re_path(
107
126
  "{label}/"
108
127
  "(?P<{identifier_label}>{identifier_pattern})/"
109
128
  "(?P<appointment>{uuid_pattern})/".format(
110
129
  **dict(
111
- label=self.label,
130
+ label=self.url_pretty_label,
112
131
  identifier_label=self.identifier_label,
113
132
  identifier_pattern=self.identifier_pattern,
114
133
  uuid_pattern=UUID_PATTERN.pattern,
115
134
  )
116
135
  ),
117
136
  self.view_class.as_view(),
118
- name=self.url_name,
137
+ name=self.url_pattern_name,
119
138
  ),
120
139
  re_path(
121
140
  "{label}/"
122
141
  "(?P<{identifier_label}>{identifier_pattern})/"
123
142
  r"(?P<schedule_name>\w+)/".format(
124
143
  **dict(
125
- label=self.label,
144
+ label=self.url_pretty_label,
126
145
  identifier_label=self.identifier_label,
127
146
  identifier_pattern=self.identifier_pattern,
128
147
  )
129
148
  ),
130
149
  self.view_class.as_view(),
131
- name=self.url_name,
150
+ name=self.url_pattern_name,
132
151
  ),
133
152
  re_path(
134
153
  "{label}/(?P<{identifier_label}>{identifier_pattern})/".format(
135
154
  **dict(
136
- label=self.label,
155
+ label=self.url_pretty_label,
137
156
  identifier_label=self.identifier_label,
138
157
  identifier_pattern=self.identifier_pattern,
139
158
  )
140
159
  ),
141
160
  self.view_class.as_view(),
142
- name=self.url_name,
161
+ name=self.url_pattern_name,
143
162
  ),
144
163
  ]
145
164
 
@@ -154,34 +173,34 @@ class UrlConfig:
154
173
  "{label}/(?P<{identifier_label}>{identifier_pattern})/"
155
174
  r"(?P<page>\d+)/".format(
156
175
  **dict(
157
- label=self.label,
176
+ label=self.url_pretty_label,
158
177
  identifier_label=self.identifier_label,
159
178
  identifier_pattern=self.identifier_pattern,
160
179
  )
161
180
  ),
162
181
  self.view_class.as_view(),
163
- name=self.url_name,
182
+ name=self.url_pattern_name,
164
183
  ),
165
184
  re_path(
166
185
  "{label}/(?P<{identifier_label}>{identifier_pattern})/".format(
167
186
  **dict(
168
- label=self.label,
187
+ label=self.url_pretty_label,
169
188
  identifier_label=self.identifier_label,
170
189
  identifier_pattern=self.identifier_pattern,
171
190
  )
172
191
  ),
173
192
  self.view_class.as_view(),
174
- name=self.url_name,
193
+ name=self.url_pattern_name,
175
194
  ),
176
195
  re_path(
177
- r"{label}/(?P<page>\d+)/".format(**dict(label=self.label)),
196
+ r"{label}/(?P<page>\d+)/".format(**dict(label=self.url_pretty_label)),
178
197
  self.view_class.as_view(),
179
- name=self.url_name,
198
+ name=self.url_pattern_name,
180
199
  ),
181
200
  re_path(
182
- r"{label}/".format(**dict(label=self.label)),
201
+ r"{label}/".format(**dict(label=self.url_pretty_label)),
183
202
  self.view_class.as_view(),
184
- name=self.url_name,
203
+ name=self.url_pattern_name,
185
204
  ),
186
205
  ]
187
206
 
@@ -192,14 +211,14 @@ class UrlConfig:
192
211
  "{label}/(?P<{identifier_label}>{identifier_pattern})/"
193
212
  "(?P<appointment>{uuid_pattern})/".format(
194
213
  **dict(
195
- label=self.label,
214
+ label=self.url_pretty_label,
196
215
  identifier_label=self.identifier_label,
197
216
  identifier_pattern=self.identifier_pattern,
198
217
  uuid_pattern=UUID_PATTERN.pattern,
199
218
  )
200
219
  ),
201
220
  self.view_class.as_view(),
202
- name=self.url_name,
221
+ name=self.url_pattern_name,
203
222
  )
204
223
  ]
205
224
  url_patterns.extend(self.listboard_urls)
@@ -16,35 +16,41 @@ class UrlNames:
16
16
  registry: dict[str, str] = field(default_factory=dict)
17
17
 
18
18
  def register(
19
- self, name: str | None = None, url: str | None = None, namespace: str | None = None
19
+ self,
20
+ key: str,
21
+ namespace: str | None = None,
22
+ url: str | None = None,
23
+ url_with_namespace: str | None = None,
20
24
  ) -> None:
21
- name = name or url
22
- complete_url = f"{namespace}:{url}" if namespace else url
23
- if name in self.registry:
24
- raise AlreadyRegistered(f"Url already registered. Got {complete_url}.")
25
- self.registry.update({name: complete_url})
25
+ url_with_namespace = url_with_namespace or f"{namespace}:{url}"
26
+ if key in self.registry:
27
+ raise AlreadyRegistered(
28
+ "Url already registered with url_names. "
29
+ f"See {key}:{self.registry[key]}. Got {url_with_namespace}."
30
+ )
31
+ self.registry.update({key: url_with_namespace})
26
32
 
27
33
  def register_from_dict(self, **urldata: str) -> None:
28
- for name, complete_url in urldata.items():
34
+ for key, url_with_namespace in urldata.items():
29
35
  try:
30
- namespace, url = complete_url.split(":")
36
+ namespace, url = url_with_namespace.split(":")
31
37
  except ValueError:
32
- namespace, url = complete_url, None
33
- self.register(name=name, url=url, namespace=namespace)
38
+ namespace, url = url_with_namespace, None
39
+ self.register(key, namespace, url=url)
34
40
 
35
41
  def all(self) -> dict[str, str]:
36
42
  return self.registry
37
43
 
38
- def get(self, name: str) -> str:
39
- if name not in self.registry:
44
+ def get(self, key: str) -> str:
45
+ if key not in self.registry:
40
46
  raise InvalidDashboardUrlName(
41
- f"Invalid dashboard url name. Expected one of {self.registry.keys()}. "
42
- f"Got '{name}'."
47
+ f"Invalid key for url_names. Expected one of {self.registry.keys()}. "
48
+ f"Got '{key}'."
43
49
  )
44
- return self.registry.get(name)
50
+ return self.registry.get(key)
45
51
 
46
- def get_or_raise(self, name: str) -> str:
47
- return self.get(name)
52
+ def get_or_raise(self, key: str) -> str:
53
+ return self.get(key)
48
54
 
49
55
 
50
56
  url_names = UrlNames()
edc_dashboard/utils.py CHANGED
@@ -5,14 +5,14 @@ from django.conf import settings
5
5
  from django.template.loader import select_template
6
6
 
7
7
 
8
- class EdcTemplateDoesNotExist(Exception):
8
+ class EdcTemplateDoesNotExist(Exception): # noqa: N818
9
9
  pass
10
10
 
11
11
 
12
12
  def get_index_page() -> int:
13
13
  index_page = getattr(settings, "INDEX_PAGE", None)
14
14
  if not index_page:
15
- warn("Settings attribute not set. See settings.INDEX_PAGE")
15
+ warn("Settings attribute not set. See settings.INDEX_PAGE", stacklevel=2)
16
16
  return getattr(settings, "INDEX_PAGE", None)
17
17
 
18
18
 
@@ -44,8 +44,8 @@ def select_edc_template(relative_path, default_app_label):
44
44
  default_path = default_app_label
45
45
  return select_template(
46
46
  [
47
- os.path.join(local_path, relative_path),
48
- os.path.join(default_path, relative_path),
47
+ str(os.path.join(local_path, relative_path)), # noqa: PTH118
48
+ str(os.path.join(default_path, relative_path)), # noqa: PTH118
49
49
  ]
50
50
  )
51
51
 
@@ -1,6 +1,3 @@
1
- from typing import Any
2
-
3
-
4
1
  class TemplateRequestContextError(Exception):
5
2
  pass
6
3
 
@@ -13,10 +10,10 @@ class TemplateRequestContextMixin:
13
10
  return [self.get_template_from_context(self.listboard_template)]
14
11
  """
15
12
 
16
- def get_context_data(self, **kwargs) -> dict[str, Any]:
17
- """Adds template data to context."""
18
- kwargs.update(self.request.template_data)
19
- return super().get_context_data(**kwargs)
13
+ # def get_context_data(self, **kwargs) -> dict[str, Any]:
14
+ # """Adds template data to context."""
15
+ # kwargs.update(self.request.template_data)
16
+ # return super().get_context_data(**kwargs)
20
17
 
21
18
  def get_template_from_context(self, key=None):
22
19
  """Returns a template_name from request.context_data."""
@@ -26,5 +23,5 @@ class TemplateRequestContextMixin:
26
23
  raise TemplateRequestContextError(
27
24
  f"Template name not defined in request context data. "
28
25
  f"Expected one of {list(self.request.template_data.keys())}. Got {e}. "
29
- )
26
+ ) from e
30
27
  return template_name
@@ -6,7 +6,6 @@ from edc_protocol.research_protocol_config import ResearchProtocolConfig
6
6
  from edc_utils.text import convert_from_camel
7
7
 
8
8
  from ..url_config import UrlConfig
9
- from ..url_names import InvalidDashboardUrlName, url_names
10
9
 
11
10
  if TYPE_CHECKING:
12
11
  from django.urls import URLPattern
@@ -23,45 +22,58 @@ class UrlRequestContextMixin:
23
22
  urlconfig_label = None
24
23
  url_name = None
25
24
 
26
- @classmethod
27
- def get_urlname(cls):
28
- return cls.url_name
25
+ # @classmethod
26
+ # def get_urlname(cls):
27
+ # return cls.url_name
29
28
 
30
29
  @classmethod
31
30
  def urls(
32
31
  cls,
33
32
  *,
34
- label: str,
35
- identifier_pattern: str,
36
- namespace: str | None = None,
33
+ namespace: str,
34
+ url_names_key: str,
37
35
  identifier_label: str | None = None,
36
+ identifier_pattern: str | None = None,
38
37
  ) -> list[URLPattern]:
39
- label = (
40
- label
38
+ """Returns a list of url patterns generated by UrlConfig.
39
+
40
+ Each url pattern has the same name; `cls.url_name`.
41
+ """
42
+ url_names_key = (
43
+ url_names_key
41
44
  or cls.urlconfig_label
42
45
  or convert_from_camel(cls.__name__.replace("view", "")).lower()
43
46
  )
44
47
  urlconfig = UrlConfig(
45
- url_name=cls.get_urlname(),
46
- namespace=namespace,
47
48
  view_class=cls,
48
- label=label,
49
+ namespace=namespace,
50
+ url_names_key=url_names_key,
49
51
  identifier_label=identifier_label or cls.urlconfig_identifier_label,
50
52
  identifier_pattern=identifier_pattern or cls.urlconfig_identifier_pattern,
51
53
  )
54
+ if cls.urlconfig_getattr not in [
55
+ "dashboard_urls",
56
+ "listboard_urls",
57
+ "review_listboard_urls",
58
+ ]:
59
+ raise UrlRequestContextError(
60
+ f"Invalid urlconfig attr. Got {cls.urlconfig_getattr}."
61
+ )
52
62
  return getattr(urlconfig, cls.urlconfig_getattr)
53
63
 
54
- @staticmethod
55
- def add_url_to_context(new_key=None, existing_key=None) -> dict[str, str]:
56
- """Add url as new_key to the context using the value
57
- of the existing_key from request.context_data.
58
- """
59
- try:
60
- url_data = {new_key: url_names.get(existing_key)}
61
- except InvalidDashboardUrlName as e:
62
- raise UrlRequestContextError(
63
- f"Url name not defined in url_names. "
64
- f"Expected one of {url_names.registry}. Got {e}. "
65
- f"Hint: check if dashboard middleware is loaded."
66
- ) from e
67
- return url_data
64
+ # @staticmethod
65
+ # def add_url_to_context(new_key=None, existing_key=None) -> dict[str, str]:
66
+ # """Add url as new_key to the context using the value
67
+ # of the existing_key from request.context_data.
68
+ # """
69
+ # if new_key != existing_key:
70
+ # try:
71
+ # url_data = {new_key: url_names.get(existing_key)}
72
+ # except InvalidDashboardUrlName as e:
73
+ # raise UrlRequestContextError(
74
+ # f"Url name not defined in url_names. "
75
+ # f"Expected one of {url_names.registry}. Got {e}. "
76
+ # f"Hint: check if dashboard middleware is loaded."
77
+ # ) from e
78
+ # return url_data
79
+ # return {existing_key: url_names.get(existing_key)}
@@ -1,5 +1,5 @@
1
+ from django.conf import settings
1
2
  from django.views.generic import TemplateView
2
-
3
3
  from edc_navbar import NavbarViewMixin
4
4
 
5
5
  from ..view_mixins import AdministrationViewMixin, EdcViewMixin
@@ -7,4 +7,4 @@ from ..view_mixins import AdministrationViewMixin, EdcViewMixin
7
7
 
8
8
  class AdministrationView(EdcViewMixin, NavbarViewMixin, AdministrationViewMixin, TemplateView):
9
9
  navbar_selected_item = "administration"
10
- navbar_name = "default" # settings.APP_NAME
10
+ navbar_name = getattr(settings, "APP_NAME", "default")
@@ -8,7 +8,7 @@ from ..view_mixins import TemplateRequestContextMixin, UrlRequestContextMixin
8
8
 
9
9
 
10
10
  class DashboardView(UrlRequestContextMixin, TemplateRequestContextMixin, TemplateView):
11
- dashboard_url_name = None
11
+ dashboard_url_name = None # see url_names dictionary
12
12
  dashboard_template = None # may be None if `dashboard_template_name` is defined
13
13
  dashboard_template_name = None # may be None if `dashboard_template` is defined
14
14
 
@@ -31,15 +31,10 @@ class DashboardView(UrlRequestContextMixin, TemplateRequestContextMixin, Templat
31
31
  return url_names.get(self.dashboard_url_name)
32
32
 
33
33
  def get_template_names(self):
34
- if self.dashboard_template_name:
35
- return [self.dashboard_template_name]
36
- return [self.get_template_from_context(self.dashboard_template)]
34
+ if self.dashboard_template:
35
+ return [self.dashboard_template]
36
+ return [self.get_template_from_context(self.dashboard_template_name)]
37
37
 
38
38
  def get_context_data(self, **kwargs) -> dict[str, Any]:
39
- kwargs.update(
40
- **self.add_url_to_context(
41
- new_key="dashboard_url_name",
42
- existing_key=self.dashboard_url_name,
43
- )
44
- )
39
+ kwargs.update(**{self.dashboard_url_name: url_names.get(self.dashboard_url_name)})
45
40
  return super().get_context_data(**kwargs)