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_label/label_template.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
from pathlib import Path
|
|
2
2
|
from string import Template
|
|
3
3
|
|
|
4
4
|
from django.apps import apps as django_apps
|
|
@@ -19,27 +19,27 @@ class LabelTemplate:
|
|
|
19
19
|
zpl_label_template = django_apps.get_model(
|
|
20
20
|
"edc_label.zpllabeltemplates"
|
|
21
21
|
).objects.get(name=self.template_name)
|
|
22
|
-
except ObjectDoesNotExist:
|
|
22
|
+
except ObjectDoesNotExist as e:
|
|
23
23
|
if static_files_path:
|
|
24
|
-
path = finders.find(
|
|
24
|
+
path = finders.find(str(Path(static_files_path) / template_name))
|
|
25
25
|
else:
|
|
26
26
|
app_config = django_apps.get_app_config("edc_label")
|
|
27
27
|
try:
|
|
28
28
|
path = app_config.label_templates[template_name]
|
|
29
|
-
except KeyError:
|
|
29
|
+
except KeyError as e:
|
|
30
30
|
raise LabelTemplateError(
|
|
31
31
|
f"Invalid label template name. "
|
|
32
32
|
f"Expected one of {list(app_config.label_templates.keys())}. "
|
|
33
33
|
f"Got '{template_name}'. "
|
|
34
34
|
f"See edc_label.app_config."
|
|
35
|
-
)
|
|
36
|
-
if not
|
|
35
|
+
) from e
|
|
36
|
+
if not path or not Path(path).exists():
|
|
37
37
|
raise LabelTemplateError(
|
|
38
38
|
f"Invalid label template path. "
|
|
39
39
|
f"Looking for template '{template_name}'. "
|
|
40
40
|
f"Got {path}. See edc_label.app_config."
|
|
41
|
-
)
|
|
42
|
-
with
|
|
41
|
+
) from e
|
|
42
|
+
with Path(path).open("r") as f:
|
|
43
43
|
self.template = f.read()
|
|
44
44
|
else:
|
|
45
45
|
self.template = zpl_label_template.zpl_data.strip()
|
edc_list_data/load_model_data.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
|
|
1
3
|
from django.apps import AppConfig
|
|
2
4
|
from django.apps import apps as django_apps
|
|
3
5
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -34,10 +36,8 @@ def load_model_data(model_data: dict, apps: AppConfig | None = None) -> int:
|
|
|
34
36
|
try:
|
|
35
37
|
obj = model.objects.get(**{unique_field: opts.get(unique_field)})
|
|
36
38
|
except ObjectDoesNotExist:
|
|
37
|
-
|
|
39
|
+
with contextlib.suppress(IntegrityError):
|
|
38
40
|
model.objects.create(**opts)
|
|
39
|
-
except IntegrityError:
|
|
40
|
-
pass
|
|
41
41
|
else:
|
|
42
42
|
for key, value in opts.items():
|
|
43
43
|
setattr(obj, key, value)
|
|
@@ -6,7 +6,7 @@ style = color_style()
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def post_migrate_list_data(sender=None, **kwargs):
|
|
9
|
-
from .site_list_data import get_autodiscover_enabled, site_list_data
|
|
9
|
+
from .site_list_data import get_autodiscover_enabled, site_list_data # noqa: PLC0415
|
|
10
10
|
|
|
11
11
|
if get_autodiscover_enabled():
|
|
12
12
|
sys.stdout.write(style.MIGRATE_HEADING("Updating list data:\n"))
|
edc_list_data/preload_data.py
CHANGED
|
@@ -11,7 +11,7 @@ style = color_style()
|
|
|
11
11
|
class PreloadData:
|
|
12
12
|
def __init__(
|
|
13
13
|
self,
|
|
14
|
-
list_data: dict[str, list[tuple[str | int, str]]]
|
|
14
|
+
list_data: dict[str, list[tuple[str | int, str]]],
|
|
15
15
|
model_data: dict | None = None,
|
|
16
16
|
list_data_model_name: str | None = None,
|
|
17
17
|
apps: AppConfig = None,
|
|
@@ -24,7 +24,7 @@ class PreloadData:
|
|
|
24
24
|
if self.model_data:
|
|
25
25
|
self.item_count += self.load_model_data()
|
|
26
26
|
|
|
27
|
-
def load_list_data(self, model_name: str
|
|
27
|
+
def load_list_data(self, model_name: str, apps: AppConfig | None = None) -> int:
|
|
28
28
|
return load_list_data(self.list_data, model_name=model_name, apps=apps)
|
|
29
29
|
|
|
30
30
|
def load_model_data(self, apps: AppConfig | None = None) -> int:
|
edc_list_data/row.py
CHANGED
|
@@ -24,7 +24,7 @@ class Row:
|
|
|
24
24
|
@dataclass
|
|
25
25
|
class AsListData:
|
|
26
26
|
data: tuple[tuple[str, str, str], ...] | Choices
|
|
27
|
-
rows: list[Row
|
|
27
|
+
rows: list[Row] | None = field(default_factory=list, init=False)
|
|
28
28
|
|
|
29
29
|
def __post_init__(self):
|
|
30
30
|
for tpl in self.data:
|
edc_list_data/site_list_data.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import copy
|
|
2
3
|
import sys
|
|
3
4
|
from importlib import import_module
|
|
@@ -51,7 +52,7 @@ class SiteListData:
|
|
|
51
52
|
if app_name and app_name in self.app_names:
|
|
52
53
|
raise AlreadyLoaded(f"App already loaded. Got {app_name}.")
|
|
53
54
|
self.app_names.append(app_name)
|
|
54
|
-
opts =
|
|
55
|
+
opts = {k: v for k, v in self._get_options(module).items()}
|
|
55
56
|
sys.stdout.write(f" - registered {self.module_name} from '{module.__name__}'\n")
|
|
56
57
|
if opts.get(self.module_name):
|
|
57
58
|
self._replace_list_data_or_raise_on_duplicate(module, opts)
|
|
@@ -60,6 +61,8 @@ class SiteListData:
|
|
|
60
61
|
def load_data(self) -> None:
|
|
61
62
|
"""Calls `load` class with each list_data dictionary module to
|
|
62
63
|
update database `list` tables.
|
|
64
|
+
|
|
65
|
+
See post_migrate_list_data.
|
|
63
66
|
"""
|
|
64
67
|
style = color_style()
|
|
65
68
|
for module_name, opts in self.registry.items():
|
|
@@ -142,10 +145,8 @@ class SiteListData:
|
|
|
142
145
|
):
|
|
143
146
|
sys.stdout.write(f" * checking sites for `{self.module_name}` ...\n")
|
|
144
147
|
for app_name in django_apps.app_configs:
|
|
145
|
-
|
|
148
|
+
with contextlib.suppress(ModuleNotFoundError):
|
|
146
149
|
self._import_and_register(app_name)
|
|
147
|
-
except ModuleNotFoundError:
|
|
148
|
-
pass
|
|
149
150
|
|
|
150
151
|
def _import_and_register(self, app_name: str) -> None:
|
|
151
152
|
mod = import_module(app_name)
|
|
@@ -155,7 +156,7 @@ class SiteListData:
|
|
|
155
156
|
except ImportError as e:
|
|
156
157
|
site_list_data.registry = before_import_registry
|
|
157
158
|
if module_has_submodule(mod, self.module_name):
|
|
158
|
-
raise SiteListDataError(f"{e} See {app_name}.{self.module_name}")
|
|
159
|
+
raise SiteListDataError(f"{e} See {app_name}.{self.module_name}") from e
|
|
159
160
|
else:
|
|
160
161
|
self.register(module, app_name=app_name)
|
|
161
162
|
|
edc_locator/auths.py
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
from edc_auth.constants import PII, PII_VIEW
|
|
2
2
|
from edc_auth.site_auths import site_auths
|
|
3
3
|
|
|
4
|
-
site_auths.update_group(
|
|
5
|
-
"edc_locator.add_subjectlocator",
|
|
6
|
-
"edc_locator.change_subjectlocator",
|
|
7
|
-
"edc_locator.view_historicalsubjectlocator",
|
|
8
|
-
"edc_locator.view_subjectlocator",
|
|
9
|
-
name=PII,
|
|
10
|
-
)
|
|
11
4
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
def update_site_auths():
|
|
6
|
+
site_auths.update_group(
|
|
7
|
+
"edc_locator.add_subjectlocator",
|
|
8
|
+
"edc_locator.change_subjectlocator",
|
|
9
|
+
"edc_locator.view_historicalsubjectlocator",
|
|
10
|
+
"edc_locator.view_subjectlocator",
|
|
11
|
+
name=PII,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
site_auths.update_group(
|
|
15
|
+
"edc_locator.view_historicalsubjectlocator",
|
|
16
|
+
"edc_locator.view_subjectlocator",
|
|
17
|
+
name=PII_VIEW,
|
|
18
|
+
)
|
|
19
|
+
site_auths.add_pii_model("edc_locator.subjectlocator")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
update_site_auths()
|
edc_metadata/auths.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
from django.apps import apps as django_apps
|
|
2
2
|
|
|
3
3
|
from edc_auth.site_auths import site_auths
|
|
4
|
+
from edc_export.constants import EXPORT
|
|
4
5
|
|
|
5
|
-
if django_apps.is_installed("edc_export"):
|
|
6
|
-
from edc_export.constants import EXPORT
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
def update_site_auths():
|
|
8
|
+
if django_apps.is_installed("edc_export"):
|
|
9
|
+
site_auths.update_group(
|
|
10
|
+
"edc_metadata.export_crfmetadata",
|
|
11
|
+
"edc_metadata.export_requisitionmetadata",
|
|
12
|
+
name=EXPORT,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
update_site_auths()
|
|
@@ -110,7 +110,7 @@ class CrfCreator(SourceModelMetadataMixin):
|
|
|
110
110
|
metadata_obj = self.metadata_model_cls.objects.create(**opts)
|
|
111
111
|
except IntegrityError as e:
|
|
112
112
|
msg = f"Integrity error creating. Tried with {opts}. Got {e}."
|
|
113
|
-
raise CreatesMetadataError(msg)
|
|
113
|
+
raise CreatesMetadataError(msg) from e
|
|
114
114
|
else:
|
|
115
115
|
if not registered:
|
|
116
116
|
metadata_obj.delete()
|
|
@@ -29,9 +29,11 @@ class MetadataRuleEvaluator:
|
|
|
29
29
|
if not self.app_labels:
|
|
30
30
|
for rule_groups in site_metadata_rules.registry.values():
|
|
31
31
|
for rule_group in rule_groups:
|
|
32
|
-
if
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
if (
|
|
33
|
+
rule_group._meta.related_visit_model == self.related_visit_model
|
|
34
|
+
and rule_group._meta.app_label not in self.app_labels
|
|
35
|
+
):
|
|
36
|
+
self.app_labels.append(rule_group._meta.app_label)
|
|
35
37
|
|
|
36
38
|
def evaluate_rules(self) -> None:
|
|
37
39
|
for app_label in self.app_labels:
|
|
@@ -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 edc_appointment.constants import MISSED_APPT
|
|
@@ -91,8 +92,6 @@ class Rule:
|
|
|
91
92
|
try:
|
|
92
93
|
field_names = [self.predicate.attr]
|
|
93
94
|
except AttributeError:
|
|
94
|
-
|
|
95
|
+
with contextlib.suppress(AttributeError):
|
|
95
96
|
field_names = self.predicate.attrs
|
|
96
|
-
except AttributeError:
|
|
97
|
-
pass
|
|
98
97
|
return field_names
|
|
@@ -50,16 +50,19 @@ class UpdatesCrfMetadataModelMixin(UpdatesMetadataModelMixin):
|
|
|
50
50
|
return django_apps.get_model(metadata_model)
|
|
51
51
|
|
|
52
52
|
@property
|
|
53
|
-
def metadata_default_entry_status(self: CrfModel) -> str:
|
|
53
|
+
def metadata_default_entry_status(self: CrfModel) -> str | None:
|
|
54
54
|
"""Returns a string that represents the default entry status
|
|
55
55
|
of the CRF in the visit schedule.
|
|
56
56
|
"""
|
|
57
57
|
crfs_prn = self.metadata_visit_object.crfs_prn
|
|
58
58
|
if self.related_visit.visit_code_sequence != 0:
|
|
59
|
-
crfs = self.metadata_visit_object.crfs_unscheduled.forms
|
|
59
|
+
crfs = (*self.metadata_visit_object.crfs_unscheduled.forms, *crfs_prn.forms)
|
|
60
60
|
else:
|
|
61
|
-
crfs = self.metadata_visit_object.crfs.forms
|
|
62
|
-
|
|
61
|
+
crfs = (*self.metadata_visit_object.crfs.forms, *crfs_prn.forms)
|
|
62
|
+
try:
|
|
63
|
+
crf = next(c for c in crfs if c.model == self._meta.label_lower)
|
|
64
|
+
except StopIteration:
|
|
65
|
+
return None
|
|
63
66
|
return REQUIRED if crf.required else NOT_REQUIRED
|
|
64
67
|
|
|
65
68
|
class Meta:
|
|
@@ -50,7 +50,7 @@ class UpdatesRequisitionMetadataModelMixin(UpdatesMetadataModelMixin):
|
|
|
50
50
|
return options
|
|
51
51
|
|
|
52
52
|
@property
|
|
53
|
-
def metadata_default_entry_status(self: RequisitionModel) -> str:
|
|
53
|
+
def metadata_default_entry_status(self: RequisitionModel) -> str | None:
|
|
54
54
|
"""Returns a string that represents the configured
|
|
55
55
|
entry status of the requisition in the visit schedule.
|
|
56
56
|
"""
|
|
@@ -64,7 +64,10 @@ class UpdatesRequisitionMetadataModelMixin(UpdatesMetadataModelMixin):
|
|
|
64
64
|
requisitions = (
|
|
65
65
|
self.metadata_visit_object.requisitions.forms + requisitions_prn.forms
|
|
66
66
|
)
|
|
67
|
-
|
|
67
|
+
try:
|
|
68
|
+
requisition = next(r for r in requisitions if r.panel.name == self.panel.name)
|
|
69
|
+
except StopIteration:
|
|
70
|
+
return None
|
|
68
71
|
return REQUIRED if requisition.required else NOT_REQUIRED
|
|
69
72
|
|
|
70
73
|
@property
|
edc_metadata/models/signals.py
CHANGED
|
@@ -94,14 +94,13 @@ def metadata_reset_on_post_delete(sender, instance, using, **kwargs) -> None:
|
|
|
94
94
|
def metadata_update_previous_timepoints_for_singleton_on_post_save(
|
|
95
95
|
sender, instance, raw, created, using, **kwargs
|
|
96
96
|
):
|
|
97
|
-
if
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
appointment = appointment.relative_previous_with_related_visit
|
|
97
|
+
if (
|
|
98
|
+
not raw
|
|
99
|
+
and not kwargs.get("update_fields")
|
|
100
|
+
and isinstance(instance, (SingletonCrfModelMixin,))
|
|
101
|
+
):
|
|
102
|
+
appointment = instance.related_visit.appointment.relative_previous_with_related_visit
|
|
103
|
+
while appointment:
|
|
104
|
+
if appointment.related_visit:
|
|
105
|
+
refresh_metadata_for_timepoint(appointment.related_visit, allow_create=False)
|
|
106
|
+
appointment = appointment.relative_previous_with_related_visit
|
edc_navbar/auths.py
CHANGED
|
@@ -4,17 +4,22 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
|
|
|
4
4
|
|
|
5
5
|
from .auth_objects import custom_codename_tuples
|
|
6
6
|
|
|
7
|
-
site_auths.add_post_update_func(
|
|
8
|
-
"edc_navbar", remove_default_model_permissions_from_edc_permissions
|
|
9
|
-
)
|
|
10
|
-
site_auths.add_custom_permissions_tuples(
|
|
11
|
-
model="edc_navbar.edcpermissions", codename_tuples=custom_codename_tuples
|
|
12
|
-
)
|
|
13
7
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
)
|
|
8
|
+
def update_site_auths():
|
|
9
|
+
site_auths.add_post_update_func(
|
|
10
|
+
"edc_navbar", remove_default_model_permissions_from_edc_permissions
|
|
11
|
+
)
|
|
12
|
+
site_auths.add_custom_permissions_tuples(
|
|
13
|
+
model="edc_navbar.edcpermissions", codename_tuples=custom_codename_tuples
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
site_auths.update_group(
|
|
17
|
+
"edc_navbar.nav_administration",
|
|
18
|
+
"edc_navbar.nav_home",
|
|
19
|
+
"edc_navbar.nav_logout",
|
|
20
|
+
"edc_navbar.nav_public",
|
|
21
|
+
name=EVERYONE,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
update_site_auths()
|
edc_notification/auths.py
CHANGED
|
@@ -6,7 +6,12 @@ from edc_export.constants import EXPORT
|
|
|
6
6
|
|
|
7
7
|
from .auth_objects import NOTIFICATION, codenames
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
site_auths.
|
|
12
|
-
|
|
9
|
+
|
|
10
|
+
def update_site_auths():
|
|
11
|
+
site_auths.add_group(*codenames, name=NOTIFICATION)
|
|
12
|
+
if django_apps.is_installed("edc_export"):
|
|
13
|
+
site_auths.update_group("edc_notification.export_notification", name=EXPORT)
|
|
14
|
+
site_auths.update_role(NOTIFICATION, name=ACCOUNT_MANAGER_ROLE)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
update_site_auths()
|
|
@@ -4,8 +4,8 @@ from .model_notification import ModelNotification
|
|
|
4
4
|
class GradedEventNotification(ModelNotification):
|
|
5
5
|
grade: int | None = None
|
|
6
6
|
model: str | None = None
|
|
7
|
-
create_fields =
|
|
8
|
-
update_fields =
|
|
7
|
+
create_fields: tuple[str] = ("ae_grade",)
|
|
8
|
+
update_fields: tuple[str] = ("ae_grade",)
|
|
9
9
|
|
|
10
10
|
def field_value_condition_on_create(self, field, current_value):
|
|
11
11
|
return str(current_value) == str(self.grade)
|
|
@@ -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.
|