clinicedc 2.0.7__py3-none-any.whl → 2.0.9__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.7.dist-info → clinicedc-2.0.9.dist-info}/METADATA +4 -3
- {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/RECORD +136 -137
- {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/WHEEL +1 -1
- edc_action_item/auths.py +37 -32
- edc_action_item/models/action_model_mixin.py +1 -2
- edc_action_item/models/signals.py +22 -23
- edc_action_item/site_action_items.py +5 -9
- edc_action_item/utils.py +3 -3
- edc_adverse_event/auths.py +55 -51
- edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +2 -4
- edc_appointment/auths.py +14 -10
- edc_appointment/creators/appointment_creator.py +1 -1
- edc_appointment/creators/appointments_creator.py +1 -1
- edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -3
- edc_appointment/model_mixins/appointment_model_mixin.py +31 -28
- edc_appointment/models/appointment.py +1 -1
- edc_appointment/utils.py +19 -24
- edc_auth/auth_objects/__init__.py +2 -20
- edc_auth/auth_objects/default_groups.py +13 -11
- edc_auth/auth_objects/default_roles.py +26 -24
- edc_auth/auth_updater/auth_updater.py +13 -2
- edc_auth/auth_updater/group_updater.py +12 -10
- edc_auth/auth_updater/role_updater.py +2 -2
- edc_auth/constants.py +10 -0
- edc_auth/import_users.py +3 -3
- edc_auth/migrations/0036_alter_userprofile_alternate_email_and_more.py +88 -0
- edc_auth/models/user_profile.py +14 -11
- edc_auth/site_auths.py +80 -67
- edc_consent/auths.py +18 -12
- edc_constants/constants.py +1 -0
- edc_crf/auths.py +5 -0
- edc_dashboard/auths.py +10 -6
- edc_dashboard/url_config.py +92 -83
- edc_dashboard/url_names.py +4 -4
- edc_dashboard/view_mixins/url_request_context_mixin.py +6 -5
- edc_data_manager/admin/data_query_admin.py +12 -11
- edc_data_manager/auths.py +37 -34
- edc_data_manager/rule/query_rule_wrapper.py +7 -7
- edc_export/archive_exporter.py +3 -2
- edc_export/auths.py +32 -28
- edc_export/model_exporter/model_exporter.py +4 -1
- edc_facility/auths.py +8 -3
- edc_facility/facility.py +8 -9
- edc_form_label/custom_label_condition.py +11 -8
- edc_form_label/form_label.py +1 -1
- edc_form_runners/auths.py +11 -6
- edc_form_validators/applicable_field_validator.py +7 -6
- edc_form_validators/base_form_validator.py +8 -9
- edc_form_validators/other_specify_field_validator.py +2 -8
- edc_form_validators/required_field_validator.py +19 -16
- edc_identifier/research_identifier.py +11 -10
- edc_identifier/simple_identifier.py +8 -2
- edc_lab/auths.py +26 -23
- edc_lab/lab/aliquot_creator.py +5 -8
- edc_lab/lab/primary_aliquot.py +14 -5
- edc_lab/migrations/0038_alter_aliquot_slug_alter_box_slug_alter_boxitem_slug_and_more.py +112 -0
- edc_lab/model_mixins/requisition/requisition_model_mixin.py +6 -8
- edc_lab/models/aliquot.py +2 -2
- edc_lab/models/manifest/manifest.py +2 -2
- edc_lab/models/manifest/manifest_item.py +1 -1
- edc_lab_dashboard/auths.py +16 -11
- edc_lab_results/calculate_missing.py +8 -8
- edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +2 -2
- edc_lab_results/get_summary.py +26 -25
- edc_lab_results/model_mixins/blood_result_model_mixin.py +2 -0
- edc_label/auths.py +6 -1
- edc_label/label_template.py +8 -8
- edc_list_data/load_model_data.py +3 -3
- edc_list_data/post_migrate_signals.py +1 -1
- edc_list_data/preload_data.py +2 -2
- edc_list_data/row.py +1 -1
- edc_list_data/site_list_data.py +6 -5
- edc_locator/auths.py +18 -13
- edc_metadata/auths.py +11 -7
- edc_metadata/metadata/metadata.py +1 -1
- edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
- edc_metadata/metadata_rules/metadata_rule_evaluator.py +5 -3
- edc_metadata/metadata_rules/rule.py +2 -3
- edc_metadata/metadata_rules/rule_evaluator.py +1 -1
- edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +7 -4
- edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +5 -2
- edc_metadata/models/signals.py +10 -11
- edc_navbar/auths.py +18 -13
- edc_notification/auths.py +9 -4
- edc_notification/notification/graded_event_notification.py +2 -2
- edc_notification/notification/model_notification.py +3 -30
- edc_notification/notification/new_model_notification.py +1 -1
- edc_notification/notification/notification.py +1 -1
- edc_notification/notification/updated_model_notification.py +2 -2
- edc_offstudy/auths.py +12 -7
- edc_pdutils/df_exporters/csv_model_exporter.py +5 -2
- edc_pharmacy/auths.py +19 -15
- edc_pharmacy/models/medication/formulation.py +5 -7
- edc_pharmacy/prescribe/create_prescription.py +3 -3
- edc_pharmacy/utils/confirm_stock.py +1 -1
- edc_pharmacy/utils/confirm_stock_at_site.py +1 -1
- edc_pharmacy/views/confirmation_at_site_view.py +6 -9
- edc_prn/admin_site.py +5 -0
- edc_prn/prn.py +10 -11
- edc_prn/urls.py +11 -0
- edc_protocol_incident/action_items.py +4 -4
- edc_protocol_incident/auths.py +27 -20
- edc_pylabels/auths.py +6 -1
- edc_qareports/auths.py +11 -7
- edc_randomization/admin.py +30 -24
- edc_randomization/auths.py +12 -7
- edc_randomization/randomizer.py +22 -20
- edc_randomization/utils.py +17 -16
- edc_refusal/auths.py +7 -2
- edc_refusal/model_mixins.py +1 -1
- edc_registration/auths.py +28 -23
- edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +13 -4
- edc_registration/models/registered_subject.py +1 -1
- edc_reportable/utils/get_reference_range_collection.py +2 -3
- edc_reportable/utils/load_data.py +1 -1
- edc_review_dashboard/auths.py +23 -18
- edc_screening/age_evaluator.py +3 -3
- edc_screening/auths.py +35 -30
- edc_screening/eligibility.py +1 -1
- edc_screening/gender_evaluator.py +1 -1
- edc_screening/model_mixins/eligibility_model_mixin.py +0 -2
- edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
- edc_screening/screening_eligibility.py +2 -4
- edc_screening/utils.py +9 -9
- edc_search/generate_slug.py +26 -0
- edc_search/model_mixins.py +10 -21
- edc_sites/auths.py +8 -3
- edc_subject_dashboard/auths.py +27 -22
- edc_timepoint/apps.py +0 -21
- edc_unblinding/auths.py +9 -4
- edc_utils/__init__.py +3 -1
- edc_utils/show_urls.py +29 -2
- edc_visit_schedule/auths.py +6 -1
- edc_visit_schedule/site_visit_schedules.py +2 -2
- edc_visit_tracking/models/signals.py +2 -2
- edc_form_label/models.py +0 -0
- edc_search/constants.py +0 -1
- edc_search/models.py +0 -0
- edc_search/search_slug.py +0 -51
- edc_search/updater.py +0 -30
- edc_search/wsgi.py +0 -7
- {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/licenses/LICENSE +0 -0
edc_registration/auths.py
CHANGED
|
@@ -4,27 +4,32 @@ from edc_auth.constants import PII, PII_VIEW
|
|
|
4
4
|
from edc_auth.site_auths import site_auths
|
|
5
5
|
from edc_export.constants import EXPORT
|
|
6
6
|
|
|
7
|
-
if django_apps.is_installed("edc_export"):
|
|
8
|
-
site_auths.update_group("edc_registration.export_registeredsubject", name=EXPORT)
|
|
9
|
-
site_auths.update_group(
|
|
10
|
-
"edc_registration.display_dob",
|
|
11
|
-
"edc_registration.display_firstname",
|
|
12
|
-
"edc_registration.display_identity",
|
|
13
|
-
"edc_registration.display_initials",
|
|
14
|
-
"edc_registration.display_lastname",
|
|
15
|
-
"edc_registration.view_historicalregisteredsubject",
|
|
16
|
-
"edc_registration.view_registeredsubject",
|
|
17
|
-
name=PII,
|
|
18
|
-
)
|
|
19
7
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
8
|
+
def update_site_auths() -> None:
|
|
9
|
+
if django_apps.is_installed("edc_export"):
|
|
10
|
+
site_auths.update_group("edc_registration.export_registeredsubject", name=EXPORT)
|
|
11
|
+
site_auths.update_group(
|
|
12
|
+
"edc_registration.display_dob",
|
|
13
|
+
"edc_registration.display_firstname",
|
|
14
|
+
"edc_registration.display_identity",
|
|
15
|
+
"edc_registration.display_initials",
|
|
16
|
+
"edc_registration.display_lastname",
|
|
17
|
+
"edc_registration.view_historicalregisteredsubject",
|
|
18
|
+
"edc_registration.view_registeredsubject",
|
|
19
|
+
name=PII,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
site_auths.update_group(
|
|
23
|
+
"edc_registration.display_dob",
|
|
24
|
+
"edc_registration.display_firstname",
|
|
25
|
+
"edc_registration.display_identity",
|
|
26
|
+
"edc_registration.display_initials",
|
|
27
|
+
"edc_registration.display_lastname",
|
|
28
|
+
"edc_registration.view_historicalregisteredsubject",
|
|
29
|
+
"edc_registration.view_registeredsubject",
|
|
30
|
+
name=PII_VIEW,
|
|
31
|
+
)
|
|
32
|
+
site_auths.add_pii_model("edc_registration.registeredsubject")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
update_site_auths()
|
|
@@ -5,7 +5,9 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
|
|
6
6
|
from django.core.exceptions import ObjectDoesNotExist
|
|
7
7
|
from django.db import models
|
|
8
|
+
from django.db.models import CharField, TextField
|
|
8
9
|
|
|
10
|
+
from edc_constants.constants import NULL_STRING
|
|
9
11
|
from edc_model import DEFAULT_BASE_FIELDS
|
|
10
12
|
|
|
11
13
|
from ..utils import get_registered_subject_model_cls
|
|
@@ -99,13 +101,20 @@ class UpdatesOrCreatesRegistrationModelMixin(models.Model):
|
|
|
99
101
|
"""
|
|
100
102
|
registration_options = {}
|
|
101
103
|
rs = self.registration_model()
|
|
102
|
-
for
|
|
103
|
-
if
|
|
104
|
+
for fname, value in self.__dict__.items():
|
|
105
|
+
if fname not in (*DEFAULT_BASE_FIELDS, "_state"):
|
|
104
106
|
try:
|
|
105
|
-
getattr(rs,
|
|
106
|
-
registration_options.update({k: v})
|
|
107
|
+
getattr(rs, fname)
|
|
107
108
|
except AttributeError:
|
|
108
109
|
pass
|
|
110
|
+
else:
|
|
111
|
+
value = ( # noqa: PLW2901
|
|
112
|
+
NULL_STRING
|
|
113
|
+
if value is None
|
|
114
|
+
and isinstance(rs._meta.get_field(fname), (CharField, TextField))
|
|
115
|
+
else value
|
|
116
|
+
)
|
|
117
|
+
registration_options.update({fname: value})
|
|
109
118
|
registration_identifier = registration_options.get("registration_identifier")
|
|
110
119
|
if registration_identifier:
|
|
111
120
|
registration_options["registration_identifier"] = self.to_string(
|
|
@@ -143,7 +143,7 @@ class RegisteredSubject(UniqueSubjectIdentifierModelMixin, SiteModelMixin, BaseU
|
|
|
143
143
|
|
|
144
144
|
def save(self, *args, **kwargs):
|
|
145
145
|
if self.identity:
|
|
146
|
-
self.additional_key =
|
|
146
|
+
self.additional_key = ""
|
|
147
147
|
self.set_uuid_as_subject_identifier_if_none()
|
|
148
148
|
self.raise_on_duplicate("subject_identifier")
|
|
149
149
|
self.raise_on_duplicate("identity")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
5
6
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -21,10 +22,8 @@ def get_reference_range_collection(obj) -> ReferenceRangeCollection:
|
|
|
21
22
|
name = obj.requisition.panel_object.reference_range_collection_name
|
|
22
23
|
except AttributeError:
|
|
23
24
|
name = obj.panel_object.reference_range_collection_name
|
|
24
|
-
|
|
25
|
+
with contextlib.suppress(ObjectDoesNotExist):
|
|
25
26
|
reference_range_collection = reference_range_colllection_model_cls().objects.get(
|
|
26
27
|
name=name
|
|
27
28
|
)
|
|
28
|
-
except ObjectDoesNotExist:
|
|
29
|
-
pass
|
|
30
29
|
return reference_range_collection
|
|
@@ -60,7 +60,7 @@ def load_reference_ranges(
|
|
|
60
60
|
collection_name: str,
|
|
61
61
|
normal_data: dict[str, list[Formula]],
|
|
62
62
|
grading_data: dict[str, list[Formula]],
|
|
63
|
-
reportable_grades: list[int],
|
|
63
|
+
reportable_grades: list[int] | None = None,
|
|
64
64
|
reportable_grades_exceptions: dict[str, list[int]] | None = None,
|
|
65
65
|
keep_existing: bool | None = None,
|
|
66
66
|
create_missing_normal: bool | None = None,
|
edc_review_dashboard/auths.py
CHANGED
|
@@ -10,23 +10,28 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
|
|
|
10
10
|
|
|
11
11
|
REVIEW = "REVIEW"
|
|
12
12
|
|
|
13
|
-
site_auths.add_post_update_func(
|
|
14
|
-
"edc_review_dashboard", remove_default_model_permissions_from_edc_permissions
|
|
15
|
-
)
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
def update_site_auths():
|
|
15
|
+
site_auths.add_post_update_func(
|
|
16
|
+
"edc_review_dashboard", remove_default_model_permissions_from_edc_permissions
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
site_auths.add_custom_permissions_tuples(
|
|
20
|
+
model="edc_review_dashboard.edcpermissions",
|
|
21
|
+
codename_tuples=[
|
|
22
|
+
(
|
|
23
|
+
"edc_review_dashboard.view_subject_review_listboard",
|
|
24
|
+
"Can access subject review listboard",
|
|
25
|
+
)
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
site_auths.add_group("edc_review_dashboard.view_subject_review_listboard", name=REVIEW)
|
|
29
|
+
|
|
30
|
+
site_auths.update_role(REVIEW, name=AUDITOR_ROLE)
|
|
31
|
+
site_auths.update_role(REVIEW, name=TMG_ROLE)
|
|
32
|
+
site_auths.update_role(REVIEW, name=CLINICIAN_ROLE)
|
|
33
|
+
site_auths.update_role(REVIEW, name=CLINICIAN_SUPER_ROLE)
|
|
34
|
+
site_auths.update_role(REVIEW, name=NURSE_ROLE)
|
|
35
|
+
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
site_auths.update_role(REVIEW, name=TMG_ROLE)
|
|
30
|
-
site_auths.update_role(REVIEW, name=CLINICIAN_ROLE)
|
|
31
|
-
site_auths.update_role(REVIEW, name=CLINICIAN_SUPER_ROLE)
|
|
32
|
-
site_auths.update_role(REVIEW, name=NURSE_ROLE)
|
|
37
|
+
update_site_auths()
|
edc_screening/age_evaluator.py
CHANGED
|
@@ -8,11 +8,11 @@ from edc_reportable.exceptions import ValueBoundryError
|
|
|
8
8
|
|
|
9
9
|
class AgeEvaluator(ReportableAgeEvaluator):
|
|
10
10
|
def __init__(self, **kwargs) -> None:
|
|
11
|
-
self.reasons_ineligible: str
|
|
11
|
+
self.reasons_ineligible: str = ""
|
|
12
12
|
super().__init__(**kwargs)
|
|
13
13
|
|
|
14
14
|
def eligible(self, age: int | None = None) -> bool:
|
|
15
|
-
self.reasons_ineligible =
|
|
15
|
+
self.reasons_ineligible = ""
|
|
16
16
|
eligible = False
|
|
17
17
|
if age:
|
|
18
18
|
try:
|
|
@@ -26,7 +26,7 @@ class AgeEvaluator(ReportableAgeEvaluator):
|
|
|
26
26
|
return eligible
|
|
27
27
|
|
|
28
28
|
def in_bounds_or_raise(self, age: int = None, **kwargs):
|
|
29
|
-
self.reasons_ineligible =
|
|
29
|
+
self.reasons_ineligible = ""
|
|
30
30
|
dob = localtime(timezone.now() - relativedelta(years=age)).date()
|
|
31
31
|
age_units = "years"
|
|
32
32
|
report_datetime = localtime(timezone.now())
|
edc_screening/auths.py
CHANGED
|
@@ -9,38 +9,43 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
|
|
|
9
9
|
|
|
10
10
|
from .auth_objects import SCREENING, SCREENING_ROLE, SCREENING_SUPER, SCREENING_VIEW
|
|
11
11
|
|
|
12
|
-
site_auths.add_post_update_func(
|
|
13
|
-
"edc_screening", remove_default_model_permissions_from_edc_permissions
|
|
14
|
-
)
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
("edc_screening.nav_screening_section", "Can access screening section"),
|
|
21
|
-
),
|
|
22
|
-
)
|
|
13
|
+
def update_site_auths() -> None:
|
|
14
|
+
site_auths.add_post_update_func(
|
|
15
|
+
"edc_screening", remove_default_model_permissions_from_edc_permissions
|
|
16
|
+
)
|
|
23
17
|
|
|
24
|
-
site_auths.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
18
|
+
site_auths.add_custom_permissions_tuples(
|
|
19
|
+
model="edc_screening.edcpermissions",
|
|
20
|
+
codename_tuples=(
|
|
21
|
+
("edc_screening.view_screening_listboard", "Can access Screening listboard"),
|
|
22
|
+
("edc_screening.nav_screening_section", "Can access screening section"),
|
|
23
|
+
),
|
|
24
|
+
)
|
|
29
25
|
|
|
30
|
-
site_auths.add_group(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
)
|
|
26
|
+
site_auths.add_group(
|
|
27
|
+
"edc_screening.view_screening_listboard",
|
|
28
|
+
"edc_screening.nav_screening_section",
|
|
29
|
+
name=SCREENING,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
site_auths.add_group(
|
|
33
|
+
"edc_screening.view_screening_listboard",
|
|
34
|
+
"edc_screening.nav_screening_section",
|
|
35
|
+
name=SCREENING_SUPER,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
site_auths.add_group(
|
|
39
|
+
"edc_screening.view_screening_listboard",
|
|
40
|
+
"edc_screening.nav_screening_section",
|
|
41
|
+
name=SCREENING_VIEW,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
site_auths.add_role(SCREENING, name=SCREENING_ROLE)
|
|
45
|
+
site_auths.update_role(SCREENING, name=CLINICIAN_ROLE)
|
|
46
|
+
site_auths.update_role(SCREENING, name=NURSE_ROLE)
|
|
47
|
+
site_auths.update_role(SCREENING_SUPER, name=CLINICIAN_SUPER_ROLE)
|
|
48
|
+
site_auths.update_role(SCREENING_VIEW, name=AUDITOR_ROLE)
|
|
35
49
|
|
|
36
|
-
site_auths.add_group(
|
|
37
|
-
"edc_screening.view_screening_listboard",
|
|
38
|
-
"edc_screening.nav_screening_section",
|
|
39
|
-
name=SCREENING_VIEW,
|
|
40
|
-
)
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
site_auths.update_role(SCREENING, name=CLINICIAN_ROLE)
|
|
44
|
-
site_auths.update_role(SCREENING, name=NURSE_ROLE)
|
|
45
|
-
site_auths.update_role(SCREENING_SUPER, name=CLINICIAN_SUPER_ROLE)
|
|
46
|
-
site_auths.update_role(SCREENING_VIEW, name=AUDITOR_ROLE)
|
|
51
|
+
update_site_auths()
|
edc_screening/eligibility.py
CHANGED
|
@@ -45,7 +45,7 @@ class Eligibility:
|
|
|
45
45
|
self.eligible = all([v for v in self.criteria.values()])
|
|
46
46
|
|
|
47
47
|
if self.eligible:
|
|
48
|
-
self.reasons_ineligible =
|
|
48
|
+
self.reasons_ineligible = ""
|
|
49
49
|
else:
|
|
50
50
|
self.reasons_ineligible = {k: v for k, v in self.criteria.items() if not v}
|
|
51
51
|
for k, v in self.criteria.items():
|
|
@@ -45,8 +45,6 @@ class EligibilityModelMixin(EligibilityFieldsModelMixin, models.Model):
|
|
|
45
45
|
"""
|
|
46
46
|
# if self.eligibility_cls:
|
|
47
47
|
self.eligibility_cls(model_obj=self)
|
|
48
|
-
# self.eligible = eligibility_obj.is_eligible
|
|
49
|
-
# self.reasons_ineligible = eligibility_obj.reasons_ineligible
|
|
50
48
|
if not self.id:
|
|
51
49
|
self.screening_identifier = self.identifier_cls().identifier
|
|
52
50
|
if self.eligible:
|
|
@@ -16,7 +16,7 @@ class ScreeningMethodsModeMixin(models.Model):
|
|
|
16
16
|
|
|
17
17
|
@staticmethod
|
|
18
18
|
def get_search_slug_fields():
|
|
19
|
-
return
|
|
19
|
+
return "screening_identifier", "subject_identifier", "reference"
|
|
20
20
|
|
|
21
21
|
@property
|
|
22
22
|
def estimated_dob(self: SubjectScreeningModelStub) -> date:
|
|
@@ -174,7 +174,7 @@ class ScreeningEligibility:
|
|
|
174
174
|
setattr(
|
|
175
175
|
self.model_obj,
|
|
176
176
|
self.reasons_ineligible_fld_name,
|
|
177
|
-
"|".join(self.reasons_ineligible.values()) or
|
|
177
|
+
"|".join(self.reasons_ineligible.values()) or "",
|
|
178
178
|
)
|
|
179
179
|
self.set_eligible_model_field()
|
|
180
180
|
self.set_fld_attrs_on_model()
|
|
@@ -229,9 +229,7 @@ class ScreeningEligibility:
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
def formatted_reasons_ineligible(self) -> str:
|
|
232
|
-
str_values = "<BR>".join(
|
|
233
|
-
[x for x in self.reasons_ineligible.values() if x is not None]
|
|
234
|
-
)
|
|
232
|
+
str_values = "<BR>".join([x for x in self.reasons_ineligible.values() if x])
|
|
235
233
|
return format_html(
|
|
236
234
|
"{}",
|
|
237
235
|
mark_safe(str_values), # nosec B703 B308
|
edc_screening/utils.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import re
|
|
4
5
|
from typing import TYPE_CHECKING, Any
|
|
5
6
|
|
|
@@ -33,14 +34,15 @@ def get_subject_screening_model_cls() -> Any:
|
|
|
33
34
|
return django_apps.get_model(get_subject_screening_model())
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def format_reasons_ineligible(*str_values: str, delimiter=None) -> str:
|
|
37
|
+
def format_reasons_ineligible(*str_values: str | None, delimiter: str | None = None) -> str:
|
|
37
38
|
reasons = None
|
|
38
39
|
delimiter = delimiter or "|"
|
|
39
|
-
str_values =
|
|
40
|
+
str_values = str_values or []
|
|
41
|
+
str_values = tuple(x for x in str_values if x)
|
|
40
42
|
if str_values:
|
|
41
43
|
reasons = format_html(
|
|
42
44
|
"{}",
|
|
43
|
-
mark_safe(delimiter.join(str_values)), #
|
|
45
|
+
mark_safe(delimiter.join(str_values)), # noqa: S308
|
|
44
46
|
)
|
|
45
47
|
return reasons
|
|
46
48
|
|
|
@@ -73,10 +75,10 @@ def get_subject_screening_or_raise(
|
|
|
73
75
|
)
|
|
74
76
|
except ObjectDoesNotExist as e:
|
|
75
77
|
if is_modelform:
|
|
76
|
-
raise forms.ValidationError("Not allowed. Screening form not found.")
|
|
78
|
+
raise forms.ValidationError("Not allowed. Screening form not found.") from e
|
|
77
79
|
raise ObjectDoesNotExist(
|
|
78
80
|
f"{e} screening_identifier={screening_identifier}. Perhaps catch this in the form."
|
|
79
|
-
)
|
|
81
|
+
) from e
|
|
80
82
|
return subject_screening
|
|
81
83
|
|
|
82
84
|
|
|
@@ -101,10 +103,8 @@ def is_eligible_or_raise(
|
|
|
101
103
|
)
|
|
102
104
|
|
|
103
105
|
url_name = url_name or "screening_listboard_url"
|
|
104
|
-
|
|
106
|
+
with contextlib.suppress(InvalidDashboardUrlName):
|
|
105
107
|
url_name = url_names.get(url_name)
|
|
106
|
-
except InvalidDashboardUrlName:
|
|
107
|
-
pass
|
|
108
108
|
|
|
109
109
|
if not subject_screening.eligible:
|
|
110
110
|
try:
|
|
@@ -128,7 +128,7 @@ def is_eligible_or_raise(
|
|
|
128
128
|
else:
|
|
129
129
|
msg = format_html(
|
|
130
130
|
'Not allowed. Subject is not eligible. See subject <A href="{}">{}</A>',
|
|
131
|
-
mark_safe(url), #
|
|
131
|
+
mark_safe(url), # noqa: S308
|
|
132
132
|
subject_screening.screening_identifier,
|
|
133
133
|
)
|
|
134
134
|
raise forms.ValidationError(msg)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.utils.text import slugify
|
|
4
|
+
from django_crypto_fields.utils import get_encrypted_field_names
|
|
5
|
+
|
|
6
|
+
SLUG_SEP = "|"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def generate_slug(obj, fields) -> str | None:
|
|
10
|
+
"""Returns a slug of the char values of the fields
|
|
11
|
+
listed on the models get_search_slug_fields() method.
|
|
12
|
+
|
|
13
|
+
Excludes any encrypted fields if listed.
|
|
14
|
+
"""
|
|
15
|
+
slug = None
|
|
16
|
+
if obj and fields:
|
|
17
|
+
values = []
|
|
18
|
+
for field in (f for f in fields if f not in get_encrypted_field_names(obj)):
|
|
19
|
+
v = obj
|
|
20
|
+
for f in field.split("."):
|
|
21
|
+
v = getattr(v, f)
|
|
22
|
+
if isinstance(v, str):
|
|
23
|
+
values.append(v[:50]) # truncate value
|
|
24
|
+
slugs = [slugify(v) for v in list(set(values)) if v]
|
|
25
|
+
slug = SLUG_SEP.join(slugs)[:250]
|
|
26
|
+
return slug
|
edc_search/model_mixins.py
CHANGED
|
@@ -1,42 +1,31 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
+
from tqdm import tqdm
|
|
2
3
|
|
|
3
|
-
from .
|
|
4
|
+
from .generate_slug import generate_slug
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class SearchSlugManager(models.Manager):
|
|
7
|
-
search_slug_updater_cls = SearchSlugUpdater
|
|
8
|
-
search_slug_field_name = "slug"
|
|
9
|
-
|
|
10
8
|
def update_search_slugs(self) -> None:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
)
|
|
15
|
-
setattr(obj, self.search_slug_field_name, updater.slug)
|
|
16
|
-
obj.save_base(update_fields=[self.search_slug_field_name])
|
|
9
|
+
qs = self.all()
|
|
10
|
+
for obj in tqdm(qs, total=qs.count()):
|
|
11
|
+
obj.slug = generate_slug(obj, obj.get_search_slug_fields()) or ""
|
|
12
|
+
obj.save_base(update_fields=["slug"])
|
|
17
13
|
|
|
18
14
|
|
|
19
15
|
class SearchSlugModelMixin(models.Model):
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def get_search_slug_fields(self) -> tuple[str] | None:
|
|
24
|
-
return None
|
|
16
|
+
def get_search_slug_fields(self) -> tuple[str, ...]:
|
|
17
|
+
return ()
|
|
25
18
|
|
|
26
19
|
slug = models.CharField(
|
|
27
20
|
max_length=250,
|
|
28
21
|
default="",
|
|
29
22
|
editable=False,
|
|
30
23
|
db_index=True,
|
|
31
|
-
help_text="
|
|
24
|
+
help_text="Hold slug field values for quick search. Excludes encrypted fields",
|
|
32
25
|
)
|
|
33
26
|
|
|
34
27
|
def save(self, *args, **kwargs):
|
|
35
|
-
|
|
36
|
-
fields=self.get_search_slug_fields(), model_obj=self
|
|
37
|
-
)
|
|
38
|
-
self.search_slug_warning = updater.warning
|
|
39
|
-
self.slug = updater.slug
|
|
28
|
+
self.slug = generate_slug(self, self.get_search_slug_fields()) or ""
|
|
40
29
|
super().save(*args, **kwargs)
|
|
41
30
|
|
|
42
31
|
class Meta:
|
edc_sites/auths.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
from edc_auth.site_auths import site_auths
|
|
2
2
|
from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
|
|
5
|
+
def update_site_auths() -> None:
|
|
6
|
+
site_auths.add_post_update_func(
|
|
7
|
+
"edc_sites", remove_default_model_permissions_from_edc_permissions
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
update_site_auths()
|
edc_subject_dashboard/auths.py
CHANGED
|
@@ -7,30 +7,35 @@ from edc_auth.constants import (
|
|
|
7
7
|
from edc_auth.site_auths import site_auths
|
|
8
8
|
from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
|
|
9
9
|
|
|
10
|
-
site_auths.add_post_update_func(
|
|
11
|
-
"edc_subject_dashboard", remove_default_model_permissions_from_edc_permissions
|
|
12
|
-
)
|
|
13
|
-
|
|
14
10
|
SUBJECT_VIEW = "SUBJECT_VIEW"
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
|
|
13
|
+
def update_site_auths() -> None:
|
|
14
|
+
site_auths.add_post_update_func(
|
|
15
|
+
"edc_subject_dashboard", remove_default_model_permissions_from_edc_permissions
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
site_auths.add_custom_permissions_tuples(
|
|
19
|
+
model="edc_subject_dashboard.edcpermissions",
|
|
20
|
+
codename_tuples=(
|
|
21
|
+
(
|
|
22
|
+
"edc_subject_dashboard.view_subject_listboard",
|
|
23
|
+
"Can access subject listboard",
|
|
24
|
+
),
|
|
25
|
+
("edc_subject_dashboard.nav_subject_section", "Can access nav_subject_section"),
|
|
22
26
|
),
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
site_auths.add_group(
|
|
30
|
+
"edc_subject_dashboard.view_subject_listboard",
|
|
31
|
+
"edc_subject_dashboard.nav_subject_section",
|
|
32
|
+
name=SUBJECT_VIEW,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_ROLE)
|
|
36
|
+
site_auths.update_role(SUBJECT_VIEW, name=NURSE_ROLE)
|
|
37
|
+
site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_SUPER_ROLE)
|
|
38
|
+
site_auths.update_role(SUBJECT_VIEW, name=AUDITOR_ROLE)
|
|
26
39
|
|
|
27
|
-
site_auths.add_group(
|
|
28
|
-
"edc_subject_dashboard.view_subject_listboard",
|
|
29
|
-
"edc_subject_dashboard.nav_subject_section",
|
|
30
|
-
name=SUBJECT_VIEW,
|
|
31
|
-
)
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
site_auths.update_role(SUBJECT_VIEW, name=NURSE_ROLE)
|
|
35
|
-
site_auths.update_role(SUBJECT_VIEW, name=CLINICIAN_SUPER_ROLE)
|
|
36
|
-
site_auths.update_role(SUBJECT_VIEW, name=AUDITOR_ROLE)
|
|
41
|
+
update_site_auths()
|
edc_timepoint/apps.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
|
|
3
3
|
from django.apps import AppConfig as DjangoAppConfig
|
|
4
|
-
from django.conf import settings
|
|
5
4
|
|
|
6
5
|
from edc_appointment.constants import COMPLETE_APPT
|
|
7
6
|
|
|
@@ -35,23 +34,3 @@ class AppConfig(DjangoAppConfig):
|
|
|
35
34
|
for model in self.timepoints:
|
|
36
35
|
sys.stdout.write(f" * '{model}' is a timepoint model.\n")
|
|
37
36
|
sys.stdout.write(f" Done loading {self.verbose_name}.\n")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if settings.APP_NAME == "edc_timepoint":
|
|
41
|
-
from dateutil.relativedelta import FR, MO, SA, SU, TH, TU, WE
|
|
42
|
-
|
|
43
|
-
from edc_facility.apps import AppConfig as BaseEdcFacilityAppConfig
|
|
44
|
-
|
|
45
|
-
class EdcFacilityAppConfig(BaseEdcFacilityAppConfig):
|
|
46
|
-
definitions = {
|
|
47
|
-
"7-day-clinic": dict(
|
|
48
|
-
days=[MO, TU, WE, TH, FR, SA, SU],
|
|
49
|
-
slots=[100, 100, 100, 100, 100, 100, 100],
|
|
50
|
-
),
|
|
51
|
-
"5-day-clinic": dict(days=[MO, TU, WE, TH, FR], slots=[100, 100, 100, 100, 100]),
|
|
52
|
-
"3-day-clinic": dict(
|
|
53
|
-
days=[TU, WE, TH],
|
|
54
|
-
slots=[100, 100, 100],
|
|
55
|
-
best_effort_available_datetime=True,
|
|
56
|
-
),
|
|
57
|
-
}
|
edc_unblinding/auths.py
CHANGED
|
@@ -9,7 +9,12 @@ from .auth_objects import (
|
|
|
9
9
|
unblinding_reviewers,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
site_auths.
|
|
15
|
-
site_auths.
|
|
12
|
+
|
|
13
|
+
def update_site_auths() -> None:
|
|
14
|
+
site_auths.add_group(*unblinding_requestors, name=UNBLINDING_REQUESTORS)
|
|
15
|
+
site_auths.add_group(*unblinding_reviewers, name=UNBLINDING_REVIEWERS)
|
|
16
|
+
site_auths.add_role(UNBLINDING_REQUESTORS, name=UNBLINDING_REQUESTORS_ROLE)
|
|
17
|
+
site_auths.add_role(UNBLINDING_REVIEWERS, name=UNBLINDING_REVIEWERS_ROLE)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
update_site_auths()
|
edc_utils/__init__.py
CHANGED
|
@@ -8,7 +8,7 @@ from .get_static_file import get_static_file
|
|
|
8
8
|
from .get_uuid import get_uuid
|
|
9
9
|
from .message_in_queue import message_in_queue
|
|
10
10
|
from .round_up import round_half_away_from_zero, round_half_up, round_up
|
|
11
|
-
from .show_urls import show_url_names, show_urls
|
|
11
|
+
from .show_urls import show_namespaces, show_url_names, show_urls, show_urls_from_patterns
|
|
12
12
|
from .text import (
|
|
13
13
|
convert_from_camel,
|
|
14
14
|
convert_php_dateformat,
|
|
@@ -48,8 +48,10 @@ __all__ = [
|
|
|
48
48
|
"round_half_up",
|
|
49
49
|
"round_up",
|
|
50
50
|
"safe_allowed_chars",
|
|
51
|
+
"show_namespaces",
|
|
51
52
|
"show_url_names",
|
|
52
53
|
"show_urls",
|
|
54
|
+
"show_urls_from_patterns",
|
|
53
55
|
"to_local",
|
|
54
56
|
"to_utc",
|
|
55
57
|
"truncate_string",
|