clinicedc 2.0.7__py3-none-any.whl → 2.0.8__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.8.dist-info}/METADATA +1 -1
- {clinicedc-2.0.7.dist-info → clinicedc-2.0.8.dist-info}/RECORD +134 -137
- {clinicedc-2.0.7.dist-info → clinicedc-2.0.8.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/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/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.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -17,10 +17,10 @@ class ModelNotification(Notification):
|
|
|
17
17
|
|
|
18
18
|
model: str | None = None # label_lower format
|
|
19
19
|
|
|
20
|
-
model_operations =
|
|
20
|
+
model_operations: tuple[str] = (CREATE, UPDATE, DELETE)
|
|
21
21
|
|
|
22
|
-
create_fields =
|
|
23
|
-
update_fields =
|
|
22
|
+
create_fields: tuple[str] = ("created",)
|
|
23
|
+
update_fields: tuple[str] = ("modified",)
|
|
24
24
|
|
|
25
25
|
email_body_template: str = (
|
|
26
26
|
"\n\nDo not reply to this email\n\n"
|
|
@@ -177,30 +177,3 @@ class ModelNotification(Notification):
|
|
|
177
177
|
)
|
|
178
178
|
|
|
179
179
|
return opts
|
|
180
|
-
|
|
181
|
-
# @property
|
|
182
|
-
# def test_template_options(self) -> dict:
|
|
183
|
-
# class Site:
|
|
184
|
-
# domain = "gaborone.example.com"
|
|
185
|
-
# name = "gaborone"
|
|
186
|
-
# id = 99
|
|
187
|
-
#
|
|
188
|
-
# class Meta:
|
|
189
|
-
# label_lower = self.model
|
|
190
|
-
#
|
|
191
|
-
# class DummyHistory:
|
|
192
|
-
# def __init__(self, objects):
|
|
193
|
-
# self._objects = objects
|
|
194
|
-
#
|
|
195
|
-
# def all(self):
|
|
196
|
-
# return self._objects
|
|
197
|
-
#
|
|
198
|
-
# class DummyInstance:
|
|
199
|
-
# id = 99
|
|
200
|
-
# subject_identifier = "123456910"
|
|
201
|
-
# site = Site()
|
|
202
|
-
# _meta = Meta()
|
|
203
|
-
#
|
|
204
|
-
# instance = DummyInstance()
|
|
205
|
-
# instance.history = DummyHistory(objects=[instance])
|
|
206
|
-
# return dict(instance=instance)
|
|
@@ -3,9 +3,9 @@ from .model_notification import ModelNotification
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class UpdatedModelNotification(ModelNotification):
|
|
6
|
-
model_operations =
|
|
6
|
+
model_operations = (UPDATE,)
|
|
7
7
|
|
|
8
|
-
update_fields =
|
|
8
|
+
update_fields = ("modified",)
|
|
9
9
|
|
|
10
10
|
email_subject_template = (
|
|
11
11
|
"*UPDATE* {test_subject_line}{protocol_name}: "
|
edc_offstudy/auths.py
CHANGED
|
@@ -8,10 +8,15 @@ from edc_auth.site_auths import site_auths
|
|
|
8
8
|
|
|
9
9
|
from .auth_objects import OFFSTUDY, OFFSTUDY_SUPER, OFFSTUDY_VIEW, codenames
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
site_auths.add_group(*codenames, name=
|
|
14
|
-
site_auths.
|
|
15
|
-
site_auths.
|
|
16
|
-
site_auths.update_role(
|
|
17
|
-
site_auths.update_role(
|
|
11
|
+
|
|
12
|
+
def update_site_auths():
|
|
13
|
+
site_auths.add_group(*codenames, name=OFFSTUDY, no_delete=True)
|
|
14
|
+
site_auths.add_group(*codenames, name=OFFSTUDY_SUPER)
|
|
15
|
+
site_auths.add_group(*codenames, name=OFFSTUDY_VIEW, view_only=True)
|
|
16
|
+
site_auths.update_role(OFFSTUDY, name=CLINICIAN_ROLE)
|
|
17
|
+
site_auths.update_role(OFFSTUDY, name=NURSE_ROLE)
|
|
18
|
+
site_auths.update_role(OFFSTUDY_SUPER, name=CLINICIAN_SUPER_ROLE)
|
|
19
|
+
site_auths.update_role(OFFSTUDY_VIEW, name=AUDITOR_ROLE)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
update_site_auths()
|
|
@@ -30,6 +30,7 @@ class CsvModelExporter:
|
|
|
30
30
|
**kwargs,
|
|
31
31
|
):
|
|
32
32
|
self.model = model or queryset.model._meta.label_lower
|
|
33
|
+
self.export_folder = export_folder
|
|
33
34
|
self.df_maker = self.df_maker_cls(
|
|
34
35
|
model=model,
|
|
35
36
|
queryset=queryset,
|
|
@@ -46,8 +47,10 @@ class CsvModelExporter:
|
|
|
46
47
|
|
|
47
48
|
def to_csv(self):
|
|
48
49
|
dataframe = self.df_maker.dataframe
|
|
49
|
-
return self.csv_exporter.to_csv(dataframe=dataframe)
|
|
50
|
+
return self.csv_exporter.to_csv(dataframe=dataframe, export_folder=self.export_folder)
|
|
50
51
|
|
|
51
52
|
def to_stata(self):
|
|
52
53
|
dataframe = self.df_maker.dataframe
|
|
53
|
-
return self.csv_exporter.to_stata(
|
|
54
|
+
return self.csv_exporter.to_stata(
|
|
55
|
+
dataframe=dataframe, export_folder=self.export_folder
|
|
56
|
+
)
|
edc_pharmacy/auths.py
CHANGED
|
@@ -18,21 +18,25 @@ from .auth_objects import (
|
|
|
18
18
|
prescriber_codenames,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
-
site_auths.add_post_update_func(
|
|
22
|
-
"edc_pharmacy", remove_default_model_permissions_from_edc_permissions
|
|
23
|
-
)
|
|
24
|
-
site_auths.add_custom_permissions_tuples(
|
|
25
|
-
model="edc_pharmacy.edcpermissions", codename_tuples=navbar_tuples
|
|
26
|
-
)
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
site_auths.
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
def update_site_auths() -> None:
|
|
23
|
+
site_auths.add_post_update_func(
|
|
24
|
+
"edc_pharmacy", remove_default_model_permissions_from_edc_permissions
|
|
25
|
+
)
|
|
26
|
+
site_auths.add_custom_permissions_tuples(
|
|
27
|
+
model="edc_pharmacy.edcpermissions", codename_tuples=navbar_tuples
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
site_auths.add_group(*pharmacy_codenames, name=PHARMACY_VIEW, view_only=True)
|
|
31
|
+
site_auths.add_group(*prescriber_codenames, name=PHARMACY_PRESCRIBER, no_delete=True)
|
|
32
|
+
site_auths.add_group(*pharmacy_site_codenames, name=PHARMACY_SITE, no_delete=False)
|
|
33
|
+
site_auths.add_group(*pharmacy_codenames, name=PHARMACY, no_delete=False)
|
|
34
|
+
|
|
35
|
+
site_auths.add_role(PHARMACY_VIEW, name=PHARMACY_AUDITOR_ROLE)
|
|
36
|
+
site_auths.add_role(PHARMACY_PRESCRIBER, name=PHARMACY_PRESCRIBER_ROLE)
|
|
37
|
+
site_auths.add_role(PHARMACY_SITE, PYLABELS, name=SITE_PHARMACIST_ROLE)
|
|
38
|
+
site_auths.add_role(PHARMACY, PYLABELS, name=PHARMACIST_ROLE)
|
|
39
|
+
site_auths.add_role(PHARMACY, name=PHARMACY_SUPER_ROLE)
|
|
32
40
|
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
site_auths.add_role(PHARMACY_PRESCRIBER, name=PHARMACY_PRESCRIBER_ROLE)
|
|
36
|
-
site_auths.add_role(PHARMACY_SITE, PYLABELS, name=SITE_PHARMACIST_ROLE)
|
|
37
|
-
site_auths.add_role(PHARMACY, PYLABELS, name=PHARMACIST_ROLE)
|
|
38
|
-
site_auths.add_role(PHARMACY, name=PHARMACY_SUPER_ROLE)
|
|
42
|
+
update_site_auths()
|
|
@@ -61,18 +61,17 @@ class Formulation(BaseUuidModel):
|
|
|
61
61
|
def save(self, *args, **kwargs):
|
|
62
62
|
self.description = self.get_description()
|
|
63
63
|
if not self.imp:
|
|
64
|
-
self.imp_description =
|
|
64
|
+
self.imp_description = ""
|
|
65
65
|
else:
|
|
66
66
|
self.imp_description = (
|
|
67
67
|
self.imp_description if self.imp_description else self.description
|
|
68
68
|
)
|
|
69
69
|
super().save(*args, **kwargs)
|
|
70
70
|
|
|
71
|
-
def get_description(self):
|
|
71
|
+
def get_description(self) -> str:
|
|
72
72
|
return (
|
|
73
73
|
f"{self.medication} {round_half_away_from_zero(self.strength, 0)}"
|
|
74
74
|
f"{self.get_units_display()} "
|
|
75
|
-
# f"{self.get_formulation_type_display()} "
|
|
76
75
|
f"{self.get_route_display()}"
|
|
77
76
|
)
|
|
78
77
|
|
|
@@ -80,7 +79,6 @@ class Formulation(BaseUuidModel):
|
|
|
80
79
|
return (
|
|
81
80
|
f"{self.medication} {round_half_away_from_zero(self.strength, 0)}"
|
|
82
81
|
f"{self.get_units_display()} "
|
|
83
|
-
# f"{self.get_formulation_type_display()} "
|
|
84
82
|
)
|
|
85
83
|
|
|
86
84
|
def get_description_with_assignment(self, assignment: Assignment) -> str:
|
|
@@ -103,9 +101,9 @@ class Formulation(BaseUuidModel):
|
|
|
103
101
|
class Meta(BaseUuidModel.Meta):
|
|
104
102
|
verbose_name = "Formulation"
|
|
105
103
|
verbose_name_plural = "Formulations"
|
|
106
|
-
constraints =
|
|
104
|
+
constraints = (
|
|
107
105
|
UniqueConstraint(
|
|
108
106
|
fields=["medication", "strength", "units", "formulation_type"],
|
|
109
107
|
name="%(app_label)s_%(class)s_med_stren_uniq",
|
|
110
|
-
)
|
|
111
|
-
|
|
108
|
+
),
|
|
109
|
+
)
|
|
@@ -30,11 +30,11 @@ def create_prescription(
|
|
|
30
30
|
for medication_name in medication_names:
|
|
31
31
|
try:
|
|
32
32
|
obj = medication_model_cls.objects.get(name__iexact=medication_name)
|
|
33
|
-
except ObjectDoesNotExist:
|
|
33
|
+
except ObjectDoesNotExist as e:
|
|
34
34
|
raise PrescriptionError(
|
|
35
35
|
"Unable to create prescription. Medication does not exist. "
|
|
36
36
|
f"Got {medication_name}"
|
|
37
|
-
)
|
|
37
|
+
) from e
|
|
38
38
|
else:
|
|
39
39
|
medications.append(obj)
|
|
40
40
|
try:
|
|
@@ -51,7 +51,7 @@ def create_prescription(
|
|
|
51
51
|
try:
|
|
52
52
|
rx = rx_model_cls.objects.create(**opts)
|
|
53
53
|
except ObjectDoesNotExist as e:
|
|
54
|
-
raise CommandError(f"Site does not exists. site_id={site_id}. Got {e}")
|
|
54
|
+
raise CommandError(f"Site does not exists. site_id={site_id}. Got {e}") from e
|
|
55
55
|
for obj in medications:
|
|
56
56
|
rx.medications.add(obj)
|
|
57
57
|
else:
|
|
@@ -15,7 +15,7 @@ def confirm_stock(
|
|
|
15
15
|
stock_codes: list[str],
|
|
16
16
|
fk_attr: str | None = None,
|
|
17
17
|
confirmed_by: str | None = None,
|
|
18
|
-
user_created: str = None,
|
|
18
|
+
user_created: str | None = None,
|
|
19
19
|
) -> tuple[list[str], list[str], list[str]]:
|
|
20
20
|
"""Confirm stock instances given a list of stock codes
|
|
21
21
|
and a request/receive pk.
|
|
@@ -26,7 +26,7 @@ def confirm_stock_at_site(
|
|
|
26
26
|
stock_transfer: StockTransfer,
|
|
27
27
|
stock_codes: list[str],
|
|
28
28
|
location: UUID,
|
|
29
|
-
request: WSGIRequest = None,
|
|
29
|
+
request: WSGIRequest | None = None,
|
|
30
30
|
) -> tuple[list[str], list[str], list[str]]:
|
|
31
31
|
"""Confirm stock instances given a list of stock codes
|
|
32
32
|
and a request/receive pk.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import uuid
|
|
4
5
|
from uuid import uuid4
|
|
5
6
|
|
|
@@ -97,13 +98,12 @@ class ConfirmationAtSiteView(
|
|
|
97
98
|
return min(unconfirmed_count, 12)
|
|
98
99
|
|
|
99
100
|
def get_stock_codes(self, stock_transfer):
|
|
100
|
-
|
|
101
|
+
return [
|
|
101
102
|
code
|
|
102
103
|
for code in stock_transfer.stocktransferitem_set.values_list(
|
|
103
104
|
"stock__code", flat=True
|
|
104
105
|
).all()
|
|
105
106
|
]
|
|
106
|
-
return stock_codes
|
|
107
107
|
|
|
108
108
|
def get_unconfirmed_count(self, stock_transfer) -> int:
|
|
109
109
|
return (
|
|
@@ -120,10 +120,8 @@ class ConfirmationAtSiteView(
|
|
|
120
120
|
def site(self) -> Site | None:
|
|
121
121
|
obj = None
|
|
122
122
|
if self.kwargs.get("site_id"):
|
|
123
|
-
|
|
123
|
+
with contextlib.suppress(ObjectDoesNotExist):
|
|
124
124
|
obj = Site.objects.get(id=self.kwargs.get("site_id"))
|
|
125
|
-
except ObjectDoesNotExist:
|
|
126
|
-
pass
|
|
127
125
|
return obj
|
|
128
126
|
|
|
129
127
|
@property
|
|
@@ -169,14 +167,13 @@ class ConfirmationAtSiteView(
|
|
|
169
167
|
def confirmation_at_site_changelist_url(self) -> str:
|
|
170
168
|
if self.confirmation_at_site:
|
|
171
169
|
url = reverse("edc_pharmacy_admin:edc_pharmacy_confirmationatsite_changelist")
|
|
172
|
-
|
|
173
|
-
return url
|
|
170
|
+
return f"{url}?q={self.confirmation_at_site.transfer_confirmation_identifier}"
|
|
174
171
|
return "/"
|
|
175
172
|
|
|
176
173
|
def get_stock_transfer(
|
|
177
174
|
self,
|
|
178
175
|
stock_transfer_identifier: str,
|
|
179
|
-
suppress_msg: bool = None,
|
|
176
|
+
suppress_msg: bool | None = None,
|
|
180
177
|
) -> StockTransfer | None:
|
|
181
178
|
stock_transfer = None
|
|
182
179
|
try:
|
|
@@ -198,7 +195,7 @@ class ConfirmationAtSiteView(
|
|
|
198
195
|
)
|
|
199
196
|
return stock_transfer
|
|
200
197
|
|
|
201
|
-
def post(self, request, *args, **kwargs) -> HttpResponseRedirect:
|
|
198
|
+
def post(self, request, *args, **kwargs) -> HttpResponseRedirect: # noqa: ARG002
|
|
202
199
|
# cancel
|
|
203
200
|
if request.POST.get("cancel") and request.POST.get("cancel") == "cancel":
|
|
204
201
|
url = reverse("edc_pharmacy:home_url")
|
edc_prn/admin_site.py
ADDED
edc_prn/prn.py
CHANGED
|
@@ -58,19 +58,18 @@ class Prn:
|
|
|
58
58
|
try:
|
|
59
59
|
return django_apps.get_model(self.model)
|
|
60
60
|
except LookupError as e:
|
|
61
|
-
raise PrnError(f"{e}. See {self!r}")
|
|
61
|
+
raise PrnError(f"{e}. See {self!r}") from e
|
|
62
62
|
|
|
63
|
-
def get_show_on_dashboard(self, subject_identifier=None, **kwargs):
|
|
63
|
+
def get_show_on_dashboard(self, subject_identifier=None, **kwargs): # noqa: ARG002
|
|
64
64
|
count = 0
|
|
65
|
-
if self.show_on_dashboard:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return True if count and self.show_on_dashboard else False
|
|
65
|
+
if self.show_on_dashboard and subject_identifier:
|
|
66
|
+
opts = dict(subject_identifier=subject_identifier)
|
|
67
|
+
try:
|
|
68
|
+
count = self.model_cls.objects.filter(**opts).count()
|
|
69
|
+
except FieldError:
|
|
70
|
+
opts = self.get_query_opts(subject_identifier)
|
|
71
|
+
count = self.model_cls.objects.filter(**opts).count()
|
|
72
|
+
return count and self.show_on_dashboard
|
|
74
73
|
|
|
75
74
|
def get_query_opts(self, subject_identifier):
|
|
76
75
|
"""Returns alternative query opts to search on
|
edc_prn/urls.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from django.urls import path
|
|
2
|
+
from django.views.generic.base import RedirectView
|
|
3
|
+
|
|
4
|
+
from .admin_site import edc_prn_admin
|
|
5
|
+
|
|
6
|
+
app_name = "edc_prn"
|
|
7
|
+
|
|
8
|
+
urlpatterns = [
|
|
9
|
+
path("admin/", edc_prn_admin.urls),
|
|
10
|
+
path("", RedirectView.as_view(url="admin/"), name="home_url"),
|
|
11
|
+
]
|
|
@@ -9,8 +9,8 @@ from .constants import (
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class ProtocolDeviationViolationAction(ActionWithNotification):
|
|
12
|
-
reference_model =
|
|
13
|
-
admin_site_name =
|
|
12
|
+
reference_model = "edc_protocol_incident.protocoldeviationviolation"
|
|
13
|
+
admin_site_name = "edc_protocol_incident_admin"
|
|
14
14
|
|
|
15
15
|
name = PROTOCOL_DEVIATION_VIOLATION_ACTION
|
|
16
16
|
display_name = "Submit Protocol Deviation / Violation Report"
|
|
@@ -25,8 +25,8 @@ class ProtocolDeviationViolationAction(ActionWithNotification):
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class ProtocolIncidentAction(ActionWithNotification):
|
|
28
|
-
reference_model =
|
|
29
|
-
admin_site_name =
|
|
28
|
+
reference_model = "edc_protocol_incident.protocolincident"
|
|
29
|
+
admin_site_name = "edc_protocol_incident_admin"
|
|
30
30
|
|
|
31
31
|
name = PROTOCOL_INCIDENT_ACTION
|
|
32
32
|
display_name = "Submit Protocol Incident Report"
|
edc_protocol_incident/auths.py
CHANGED
|
@@ -14,24 +14,31 @@ from .auth_objects import (
|
|
|
14
14
|
)
|
|
15
15
|
from .constants import PROTOCOL_DEVIATION_VIOLATION, PROTOCOL_INCIDENT
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
site_auths.add_group(*protocol_incident_codenames, name=PROTOCOL_INCIDENT)
|
|
22
|
-
site_auths.add_group(*protocol_incident_view_codenames, name=PROTOCOL_INCIDENT_VIEW)
|
|
23
|
-
|
|
24
|
-
if incident_type == PROTOCOL_DEVIATION_VIOLATION:
|
|
25
|
-
site_auths.update_role(PROTOCOL_VIOLATION, name=CLINICIAN_ROLE)
|
|
26
|
-
site_auths.update_role(PROTOCOL_VIOLATION, name=CLINICIAN_SUPER_ROLE)
|
|
27
|
-
site_auths.update_role(PROTOCOL_VIOLATION_VIEW, name=AUDITOR_ROLE)
|
|
28
|
-
elif incident_type == PROTOCOL_INCIDENT:
|
|
29
|
-
site_auths.update_role(PROTOCOL_INCIDENT, name=CLINICIAN_ROLE)
|
|
30
|
-
site_auths.update_role(PROTOCOL_INCIDENT, name=CLINICIAN_SUPER_ROLE)
|
|
31
|
-
site_auths.update_role(PROTOCOL_INCIDENT_VIEW, name=AUDITOR_ROLE)
|
|
32
|
-
else:
|
|
33
|
-
raise ValueError(
|
|
34
|
-
"Invalid value for settings.EDC_PROTOCOL_VIOLATION_TYPE. "
|
|
35
|
-
f"Expected `{PROTOCOL_INCIDENT}` or `{PROTOCOL_DEVIATION_VIOLATION}`. "
|
|
36
|
-
f"Got {incident_type}."
|
|
17
|
+
|
|
18
|
+
def update_site_auths() -> None:
|
|
19
|
+
incident_type = getattr(
|
|
20
|
+
settings, "EDC_PROTOCOL_VIOLATION_TYPE", PROTOCOL_DEVIATION_VIOLATION
|
|
37
21
|
)
|
|
22
|
+
|
|
23
|
+
site_auths.add_group(*protocol_violation_codenames, name=PROTOCOL_VIOLATION)
|
|
24
|
+
site_auths.add_group(*protocol_violation_view_codenames, name=PROTOCOL_VIOLATION_VIEW)
|
|
25
|
+
site_auths.add_group(*protocol_incident_codenames, name=PROTOCOL_INCIDENT)
|
|
26
|
+
site_auths.add_group(*protocol_incident_view_codenames, name=PROTOCOL_INCIDENT_VIEW)
|
|
27
|
+
|
|
28
|
+
if incident_type == PROTOCOL_DEVIATION_VIOLATION:
|
|
29
|
+
site_auths.update_role(PROTOCOL_VIOLATION, name=CLINICIAN_ROLE)
|
|
30
|
+
site_auths.update_role(PROTOCOL_VIOLATION, name=CLINICIAN_SUPER_ROLE)
|
|
31
|
+
site_auths.update_role(PROTOCOL_VIOLATION_VIEW, name=AUDITOR_ROLE)
|
|
32
|
+
elif incident_type == PROTOCOL_INCIDENT:
|
|
33
|
+
site_auths.update_role(PROTOCOL_INCIDENT, name=CLINICIAN_ROLE)
|
|
34
|
+
site_auths.update_role(PROTOCOL_INCIDENT, name=CLINICIAN_SUPER_ROLE)
|
|
35
|
+
site_auths.update_role(PROTOCOL_INCIDENT_VIEW, name=AUDITOR_ROLE)
|
|
36
|
+
else:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
"Invalid value for settings.EDC_PROTOCOL_VIOLATION_TYPE. "
|
|
39
|
+
f"Expected `{PROTOCOL_INCIDENT}` or `{PROTOCOL_DEVIATION_VIOLATION}`. "
|
|
40
|
+
f"Got {incident_type}."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
update_site_auths()
|
edc_pylabels/auths.py
CHANGED
|
@@ -2,4 +2,9 @@ from edc_auth.site_auths import site_auths
|
|
|
2
2
|
|
|
3
3
|
from .auth_objects import PYLABELS, codenames
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
def update_site_auths() -> None:
|
|
7
|
+
site_auths.add_group(*codenames, name=PYLABELS, no_delete=False)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
update_site_auths()
|
edc_qareports/auths.py
CHANGED
|
@@ -9,12 +9,16 @@ from .auth_objects import (
|
|
|
9
9
|
qa_reports_codenames,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
# groups
|
|
13
|
-
site_auths.add_group(*qa_reports_codenames, name=QA_REPORTS)
|
|
14
|
-
site_auths.add_group(*qa_reports_codenames, name=QA_REPORTS_AUDIT, view_only=True)
|
|
15
12
|
|
|
13
|
+
def update_site_auths() -> None:
|
|
14
|
+
# groups
|
|
15
|
+
site_auths.add_group(*qa_reports_codenames, name=QA_REPORTS)
|
|
16
|
+
site_auths.add_group(*qa_reports_codenames, name=QA_REPORTS_AUDIT, view_only=True)
|
|
16
17
|
|
|
17
|
-
# roles
|
|
18
|
-
site_auths.add_role(QA_REPORTS, name=QA_REPORTS_ROLE)
|
|
19
|
-
site_auths.add_role(QA_REPORTS, name=QA_REPORTS_SUPER_ROLE)
|
|
20
|
-
site_auths.add_role(QA_REPORTS_AUDIT, name=QA_REPORTS_AUDIT_ROLE)
|
|
18
|
+
# roles
|
|
19
|
+
site_auths.add_role(QA_REPORTS, name=QA_REPORTS_ROLE)
|
|
20
|
+
site_auths.add_role(QA_REPORTS, name=QA_REPORTS_SUPER_ROLE)
|
|
21
|
+
site_auths.add_role(QA_REPORTS_AUDIT, name=QA_REPORTS_AUDIT_ROLE)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
update_site_auths()
|
edc_randomization/admin.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
|
|
1
3
|
from django.conf import settings
|
|
2
4
|
from django.contrib import admin
|
|
3
5
|
from django.contrib.admin.sites import AlreadyRegistered
|
|
@@ -65,7 +67,8 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
65
67
|
|
|
66
68
|
def get_readonly_fields(self, request, obj=None):
|
|
67
69
|
readonly_fields = super().get_readonly_fields(request, obj=obj)
|
|
68
|
-
readonly_fields
|
|
70
|
+
readonly_fields = (
|
|
71
|
+
*readonly_fields,
|
|
69
72
|
"subject_identifier",
|
|
70
73
|
"sid",
|
|
71
74
|
"site_name",
|
|
@@ -75,7 +78,8 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
75
78
|
"allocated_datetime",
|
|
76
79
|
"allocated_site",
|
|
77
80
|
"randomizer_name",
|
|
78
|
-
|
|
81
|
+
*audit_fields,
|
|
82
|
+
)
|
|
79
83
|
return tuple(set(readonly_fields))
|
|
80
84
|
|
|
81
85
|
def get_queryset(self, request):
|
|
@@ -90,7 +94,7 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
90
94
|
return qs
|
|
91
95
|
|
|
92
96
|
def get_list_display(self, request) -> tuple[str, ...]:
|
|
93
|
-
|
|
97
|
+
fields = (
|
|
94
98
|
"sid",
|
|
95
99
|
"assignment",
|
|
96
100
|
"site_name",
|
|
@@ -98,22 +102,24 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
98
102
|
"allocated_datetime",
|
|
99
103
|
"allocated_site",
|
|
100
104
|
"randomizer_name",
|
|
101
|
-
|
|
105
|
+
)
|
|
106
|
+
if flds := site_randomizers.get_by_model(
|
|
107
|
+
self.model._meta.label_lower
|
|
108
|
+
).get_extra_list_display():
|
|
109
|
+
fields = list(fields)
|
|
110
|
+
for pos, fname in flds:
|
|
111
|
+
fields.insert(pos, fname)
|
|
112
|
+
fields = tuple(fields)
|
|
102
113
|
if user_is_blinded(request.user.username) or (
|
|
103
114
|
not user_is_blinded(request.user.username)
|
|
104
115
|
and RANDO_UNBLINDED not in [g.name for g in request.user.groups.all()]
|
|
105
116
|
):
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
self.model._meta.label_lower
|
|
109
|
-
).get_extra_list_display():
|
|
110
|
-
for pos, fldname in flds:
|
|
111
|
-
list_display.insert(pos, fldname)
|
|
112
|
-
return tuple(list_display)
|
|
117
|
+
fields = tuple([fname for fname in fields if fname != "assignment"])
|
|
118
|
+
return fields
|
|
113
119
|
|
|
114
120
|
@staticmethod
|
|
115
121
|
def get_fieldnames(request) -> tuple[str, ...]:
|
|
116
|
-
fields =
|
|
122
|
+
fields = (
|
|
117
123
|
"subject_identifier",
|
|
118
124
|
"sid",
|
|
119
125
|
"assignment",
|
|
@@ -122,33 +128,35 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
122
128
|
"allocated_datetime",
|
|
123
129
|
"allocated_site",
|
|
124
130
|
"randomizer_name",
|
|
125
|
-
|
|
131
|
+
)
|
|
126
132
|
if user_is_blinded(request.user.username) or (
|
|
127
133
|
not user_is_blinded(request.user.username)
|
|
128
134
|
and RANDO_UNBLINDED not in [g.name for g in request.user.groups.all()]
|
|
129
135
|
):
|
|
130
|
-
fields
|
|
131
|
-
return
|
|
136
|
+
fields = tuple([fname for fname in fields if fname != "assignment"])
|
|
137
|
+
return fields
|
|
132
138
|
|
|
133
139
|
def get_list_filter(self, request) -> tuple[str, ...]:
|
|
134
|
-
|
|
140
|
+
fields = (
|
|
135
141
|
"assignment",
|
|
136
142
|
"allocated_datetime",
|
|
137
143
|
"allocated_site",
|
|
138
144
|
"site_name",
|
|
139
145
|
"randomizer_name",
|
|
140
|
-
|
|
146
|
+
)
|
|
141
147
|
if flds := site_randomizers.get_by_model(
|
|
142
148
|
self.model._meta.label_lower
|
|
143
149
|
).get_extra_list_filter():
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
fields = list(fields)
|
|
151
|
+
for pos, fname in flds:
|
|
152
|
+
fields.insert(pos, fname)
|
|
153
|
+
fields = tuple(fields)
|
|
146
154
|
if user_is_blinded(request.user.username) or (
|
|
147
155
|
not user_is_blinded(request.user.username)
|
|
148
156
|
and RANDO_UNBLINDED not in [g.name for g in request.user.groups.all()]
|
|
149
157
|
):
|
|
150
|
-
|
|
151
|
-
return
|
|
158
|
+
fields = tuple([fname for fname in fields if fname != "assignment"])
|
|
159
|
+
return fields
|
|
152
160
|
|
|
153
161
|
|
|
154
162
|
def register_admin():
|
|
@@ -156,10 +164,8 @@ def register_admin():
|
|
|
156
164
|
for randomizer_cls in site_randomizers._registry.values():
|
|
157
165
|
model = randomizer_cls.model_cls()
|
|
158
166
|
admin_cls = type(f"{model.__name__}ModelAdmin", (RandomizationListModelAdmin,), {})
|
|
159
|
-
|
|
167
|
+
with contextlib.suppress(AlreadyRegistered):
|
|
160
168
|
edc_randomization_admin.register(model, admin_cls)
|
|
161
|
-
except AlreadyRegistered:
|
|
162
|
-
pass
|
|
163
169
|
|
|
164
170
|
|
|
165
171
|
register_admin()
|
edc_randomization/auths.py
CHANGED
|
@@ -9,11 +9,16 @@ from .auth_objects import (
|
|
|
9
9
|
update_rando_group_permissions,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
site_auths.add_post_update_func(
|
|
13
|
-
"edc_randomization", remove_default_model_permissions_from_edc_permissions
|
|
14
|
-
)
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
site_auths.
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
def update_site_auths() -> None:
|
|
14
|
+
site_auths.add_post_update_func(
|
|
15
|
+
"edc_randomization", remove_default_model_permissions_from_edc_permissions
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
site_auths.add_group(get_rando_permissions_codenames, name=RANDO_BLINDED, view_only=True)
|
|
19
|
+
site_auths.add_group(get_rando_permissions_codenames, name=RANDO_UNBLINDED, view_only=True)
|
|
20
|
+
site_auths.add_post_update_func("edc_randomization", update_rando_group_permissions)
|
|
21
|
+
site_auths.add_post_update_func("edc_randomization", make_randomizationlist_view_only)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
update_site_auths()
|