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
|
@@ -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(
|
|
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
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
|
|
7
|
+
ordering = ("display_index", "display_name")
|
|
8
8
|
|
|
9
|
-
list_display
|
|
9
|
+
list_display = ("display_name", "name", "display_index")
|
|
10
10
|
|
|
11
|
-
search_fields
|
|
11
|
+
search_fields = ("display_name", "name")
|
edc_list_data/load_model_data.py
CHANGED
|
@@ -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
|
edc_list_data/site_list_data.py
CHANGED
|
@@ -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 =
|
|
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
|
|
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)
|
edc_listboard/middleware.py
CHANGED
|
@@ -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)
|
|
14
|
-
template_data =
|
|
15
|
-
|
|
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
|
|
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
|
|
@@ -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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
@@ -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
|
edc_ltfu/modeladmin_mixin.py
CHANGED
edc_ltfu/modelform_mixins.py
CHANGED
|
@@ -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
|
-
|
|
201
|
+
url = None
|
|
201
202
|
if hasattr(obj, "get_absolute_url"):
|
|
202
203
|
url = reverse(self.changelist_url)
|
|
203
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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:
|
edc_metadata/metadata_handler.py
CHANGED
|
@@ -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
|
-
|
|
79
|
-
except
|
|
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
|
|
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
|
|
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]
|
|
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
|
|
33
|
-
consequence: str
|
|
34
|
-
alternative: str
|
|
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
|
-
|
|
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])
|