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.
- {clinicedc-2.0.38.dist-info → clinicedc-2.0.40.dist-info}/METADATA +3 -12
- {clinicedc-2.0.38.dist-info → clinicedc-2.0.40.dist-info}/RECORD +146 -153
- {clinicedc-2.0.38.dist-info → clinicedc-2.0.40.dist-info}/WHEEL +1 -1
- edc_adverse_event/dashboard_urls.py +2 -0
- edc_adverse_event/middleware.py +7 -6
- edc_adverse_event/navbars.py +4 -8
- edc_adverse_event/templates/edc_adverse_event/tmg/tmg_ae_listboard_result.html +27 -23
- edc_adverse_event/templatetags/edc_adverse_event_extras.py +21 -34
- edc_adverse_event/urls.py +14 -6
- edc_adverse_event/view_mixins/ae/ae_listboard_view_mixin.py +6 -8
- edc_adverse_event/view_mixins/ae/death_report_listboard_view_mixin.py +2 -4
- edc_adverse_event/view_mixins/tmg/tmg_ae_listboard_view_mixin.py +9 -7
- edc_adverse_event/views/home_view.py +1 -2
- edc_adverse_event/views/tmg/death_listboard_view.py +8 -6
- edc_adverse_event/views/tmg/home_view.py +4 -3
- edc_adverse_event/views/tmg/summary_listboard_view.py +4 -4
- edc_appointment/utils.py +3 -6
- edc_appointment/views/unscheduled_appointment_view.py +1 -1
- edc_consent/form_validators/consent_definition_form_validator_mixin.py +5 -2
- edc_consent/model_mixins/consent_version_model_mixin.py +1 -1
- edc_consent/navbars.py +2 -1
- edc_crf/model_mixins/crf_model_mixin.py +5 -1
- edc_crf/model_mixins/crf_no_manager_model_mixin.py +2 -2
- edc_crf/model_mixins/singleton_crf_model_mixin.py +1 -1
- edc_dashboard/middleware.py +10 -16
- edc_dashboard/middleware_mixins.py +10 -0
- edc_dashboard/navbars.py +1 -1
- edc_dashboard/url_config.py +50 -31
- edc_dashboard/url_names.py +23 -17
- edc_dashboard/utils.py +4 -4
- edc_dashboard/view_mixins/template_request_context_mixin.py +5 -8
- edc_dashboard/view_mixins/url_request_context_mixin.py +38 -26
- edc_dashboard/views/administration_view.py +2 -2
- edc_dashboard/views/dashboard_view.py +5 -10
- edc_data_manager/handlers/handlers.py +17 -5
- edc_data_manager/models/query_rule.py +7 -7
- edc_data_manager/navbar_item.py +1 -1
- edc_data_manager/rule/query_rule_wrapper.py +1 -1
- edc_data_manager/rule/rule_runner.py +6 -6
- edc_device/navbars.py +1 -1
- edc_export/navbars.py +2 -2
- edc_glucose/model_mixin_factories/fasting_model_mixin_factory.py +1 -1
- edc_identifier/identifier.py +6 -9
- edc_lab_dashboard/dashboard_urls.py +7 -5
- edc_lab_dashboard/middleware.py +10 -17
- edc_lab_dashboard/navbars.py +9 -9
- edc_lab_dashboard/templates/edc_lab_dashboard/listboard/tags/status_column.html +7 -0
- edc_lab_dashboard/urls.py +2 -5
- edc_lab_dashboard/view_mixins/form_action_view_mixin.py +1 -2
- edc_lab_dashboard/views/action_views/action_view.py +6 -6
- edc_lab_dashboard/views/action_views/aliquot_view.py +1 -1
- edc_lab_dashboard/views/action_views/manage_box_item_view.py +2 -3
- edc_lab_dashboard/views/action_views/manage_manifest_view.py +1 -1
- edc_lab_dashboard/views/action_views/manifest_view.py +2 -2
- edc_lab_dashboard/views/action_views/pack_view.py +2 -2
- edc_lab_dashboard/views/action_views/process_view.py +1 -1
- edc_lab_dashboard/views/action_views/receive_view.py +1 -1
- edc_lab_dashboard/views/action_views/requisition_view.py +1 -1
- edc_lab_dashboard/views/action_views/verify_box_item_view.py +1 -1
- edc_lab_dashboard/views/listboard_views/manage_box_listboard_view.py +4 -5
- edc_lab_dashboard/views/listboard_views/manifest_listboard_view.py +5 -6
- edc_lab_dashboard/views/listboard_views/process_listboard_view.py +4 -5
- edc_lab_dashboard/views/listboard_views/receive_listboard_view.py +5 -6
- edc_lab_dashboard/views/listboard_views/verify_box_listboard_view.py +5 -6
- edc_label/navbars.py +1 -1
- edc_list_data/admin.py +3 -3
- edc_list_data/load_model_data.py +1 -1
- edc_list_data/management/commands/load_list_data.py +2 -2
- edc_list_data/site_list_data.py +4 -4
- edc_listboard/middleware.py +9 -8
- edc_listboard/templates/edc_listboard/listboard.html +1 -1
- edc_listboard/view_mixins/listboard_filter_view_mixin.py +1 -1
- edc_listboard/view_mixins/search_form_view_mixin.py +1 -1
- edc_listboard/views/listboard_view.py +16 -25
- edc_listboard/views/screen/screening_listboard_view.py +2 -2
- edc_listboard/views/subject/subject_listboard_view.py +2 -2
- edc_locator/forms/subject_locator_form_validator.py +2 -2
- edc_ltfu/action_items.py +1 -2
- edc_ltfu/forms/ltfu_form_validator_mixin.py +3 -3
- edc_ltfu/modeladmin_mixin.py +1 -1
- edc_ltfu/modelform_mixins.py +2 -2
- edc_metadata/admin/modeladmin_mixins.py +11 -9
- edc_metadata/management/commands/update_metadata.py +1 -1
- edc_metadata/management/commands/update_metadata_schedule_names.py +7 -7
- edc_metadata/management/commands/validate_entry_status.py +1 -1
- edc_metadata/management/commands/validate_rule_groups.py +1 -1
- edc_metadata/metadata/metadata_getter.py +3 -5
- edc_metadata/metadata_handler.py +5 -5
- edc_metadata/metadata_mixins/source_model_metadata_mixin.py +1 -1
- edc_metadata/metadata_refresher.py +1 -1
- edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
- edc_metadata/metadata_rules/logic.py +3 -3
- edc_metadata/metadata_rules/persistant_singleton_mixin.py +2 -4
- edc_metadata/metadata_rules/requisition/requisition_rule_group.py +1 -1
- edc_metadata/metadata_rules/rule.py +4 -3
- edc_metadata/metadata_rules/rule_group.py +2 -2
- edc_metadata/metadata_rules/rule_group_meta_options.py +2 -2
- edc_metadata/metadata_rules/rule_group_metaclass.py +21 -22
- edc_metadata/metadata_rules/site.py +1 -1
- edc_metadata/metadata_updater.py +4 -3
- edc_metadata/model_mixins/creates/creates_metadata_model_mixin.py +3 -5
- edc_metadata/model_mixins/updates/updates_metadata_model_mixin.py +1 -1
- edc_metadata/next_form_getter.py +15 -19
- edc_metadata/offline_models.py +1 -1
- edc_metadata/requisition/requisition_metadata_handler.py +5 -5
- edc_metadata/update_metadata_on_schedule_change.py +2 -4
- edc_metadata/utils.py +1 -1
- edc_model/models/signals.py +7 -2
- edc_model_admin/mixins/model_admin_redirect_on_delete_mixin.py +4 -3
- edc_navbar/apps.py +0 -2
- edc_navbar/navbar.py +1 -1
- edc_navbar/navbar_item.py +29 -16
- edc_navbar/navbars.py +6 -19
- edc_navbar/site_navbars.py +6 -7
- edc_navbar/system_checks.py +3 -10
- edc_navbar/utils.py +14 -0
- edc_navbar/view_mixin.py +6 -9
- edc_pharmacy/navbars.py +1 -1
- edc_pharmacy/views/confirm_stock_from_queryset_view.py +3 -3
- edc_protocol/middleware.py +9 -13
- edc_protocol/navbars.py +1 -1
- edc_refusal/forms.py +1 -3
- edc_reportable/utils/convert_units.py +1 -1
- edc_review_dashboard/middleware.py +6 -3
- edc_review_dashboard/navbars.py +1 -2
- edc_review_dashboard/urls.py +3 -2
- edc_review_dashboard/views/subject_review_listboard_view.py +4 -2
- edc_subject_dashboard/dashboard_templates.py +1 -3
- edc_subject_dashboard/dashboard_urls.py +8 -0
- edc_subject_dashboard/middleware.py +10 -7
- edc_subject_dashboard/templates/edc_subject_dashboard/buttons/refresh_appointments_button.html +1 -1
- edc_subject_dashboard/templates/edc_subject_dashboard/dashboard.html +1 -1
- edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +3 -1
- edc_subject_dashboard/urls.py +13 -4
- edc_subject_dashboard/views/base_requisition_view.py +2 -1
- edc_subject_dashboard/views/subject_dashboard_view.py +1 -2
- edc_timepoint/__init__.py +0 -2
- edc_timepoint/model_mixins.py +1 -2
- edc_timepoint/utils.py +1 -1
- edc_timepoint/visit_timepoint_lookup.py +6 -0
- edc_visit_schedule/admin/subject_schedule_history_admin.py +1 -2
- edc_visit_schedule/navbars.py +3 -4
- edc_visit_schedule/visit/visit.py +15 -0
- edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +5 -0
- edc_visit_tracking/models/subject_visit.py +5 -0
- edc_lab_dashboard/model_wrappers/__init__.py +0 -8
- edc_lab_dashboard/model_wrappers/aliquot_model_wrapper.py +0 -31
- edc_lab_dashboard/model_wrappers/base_box_item_model_wrapper.py +0 -21
- edc_lab_dashboard/model_wrappers/box_model_wrapper.py +0 -12
- edc_lab_dashboard/model_wrappers/manage_box_item_model_wrapper.py +0 -6
- edc_lab_dashboard/model_wrappers/manifest_item_model_wrapper.py +0 -21
- edc_lab_dashboard/model_wrappers/manifest_model_wrapper.py +0 -11
- edc_lab_dashboard/model_wrappers/requisition_model_wrapper.py +0 -25
- edc_lab_dashboard/model_wrappers/result_model_wrapper.py +0 -8
- edc_lab_dashboard/model_wrappers/verify_box_model_wrapper.py +0 -10
- edc_navbar/get_default_navbar.py +0 -9
- {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,
|
|
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
|
@@ -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
|
|
edc_dashboard/middleware.py
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
edc_dashboard/url_config.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
38
|
-
url_names.register(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
193
|
+
name=self.url_pattern_name,
|
|
175
194
|
),
|
|
176
195
|
re_path(
|
|
177
|
-
r"{label}/(?P<page>\d+)/".format(**dict(label=self.
|
|
196
|
+
r"{label}/(?P<page>\d+)/".format(**dict(label=self.url_pretty_label)),
|
|
178
197
|
self.view_class.as_view(),
|
|
179
|
-
name=self.
|
|
198
|
+
name=self.url_pattern_name,
|
|
180
199
|
),
|
|
181
200
|
re_path(
|
|
182
|
-
r"{label}/".format(**dict(label=self.
|
|
201
|
+
r"{label}/".format(**dict(label=self.url_pretty_label)),
|
|
183
202
|
self.view_class.as_view(),
|
|
184
|
-
name=self.
|
|
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.
|
|
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.
|
|
221
|
+
name=self.url_pattern_name,
|
|
203
222
|
)
|
|
204
223
|
]
|
|
205
224
|
url_patterns.extend(self.listboard_urls)
|
edc_dashboard/url_names.py
CHANGED
|
@@ -16,35 +16,41 @@ class UrlNames:
|
|
|
16
16
|
registry: dict[str, str] = field(default_factory=dict)
|
|
17
17
|
|
|
18
18
|
def register(
|
|
19
|
-
self,
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
34
|
+
for key, url_with_namespace in urldata.items():
|
|
29
35
|
try:
|
|
30
|
-
namespace, url =
|
|
36
|
+
namespace, url = url_with_namespace.split(":")
|
|
31
37
|
except ValueError:
|
|
32
|
-
namespace, url =
|
|
33
|
-
self.register(
|
|
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,
|
|
39
|
-
if
|
|
44
|
+
def get(self, key: str) -> str:
|
|
45
|
+
if key not in self.registry:
|
|
40
46
|
raise InvalidDashboardUrlName(
|
|
41
|
-
f"Invalid
|
|
42
|
-
f"Got '{
|
|
47
|
+
f"Invalid key for url_names. Expected one of {self.registry.keys()}. "
|
|
48
|
+
f"Got '{key}'."
|
|
43
49
|
)
|
|
44
|
-
return self.registry.get(
|
|
50
|
+
return self.registry.get(key)
|
|
45
51
|
|
|
46
|
-
def get_or_raise(self,
|
|
47
|
-
return self.get(
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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"
|
|
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.
|
|
35
|
-
return [self.
|
|
36
|
-
return [self.get_template_from_context(self.
|
|
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)
|