clinicedc 2.0.28__py3-none-any.whl → 2.0.30__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.28.dist-info → clinicedc-2.0.30.dist-info}/METADATA +2 -2
- {clinicedc-2.0.28.dist-info → clinicedc-2.0.30.dist-info}/RECORD +98 -98
- edc_auth/admin/fieldsets.py +11 -15
- edc_auth/admin/group_admin.py +2 -5
- edc_auth/admin/list_filters.py +8 -7
- edc_auth/admin/role_admin.py +7 -10
- edc_auth/admin/user_admin.py +2 -2
- edc_auth/admin/user_profile_admin.py +3 -7
- edc_auth/apps.py +2 -2
- edc_auth/auth_updater/group_updater.py +4 -4
- edc_auth/auth_updater/role_updater.py +1 -3
- edc_auth/backends.py +1 -1
- edc_auth/export_users.py +6 -5
- edc_auth/fix_export_permissions.py +8 -8
- edc_auth/forms.py +21 -24
- edc_auth/get_app_codenames.py +2 -2
- edc_auth/import_users.py +25 -24
- edc_auth/management/commands/export_users.py +1 -1
- edc_auth/management/commands/fix_export_permissions.py +1 -1
- edc_auth/management/commands/import_users.py +1 -1
- edc_auth/management/commands/reset_password.py +2 -2
- edc_auth/models/signals.py +1 -1
- edc_auth/models/user_profile.py +4 -5
- edc_auth/password_setter.py +4 -4
- edc_auth/post_migrate_signals.py +2 -2
- edc_auth/send_new_credentials_to_user.py +1 -1
- edc_auth/system_checks.py +7 -6
- edc_auth/utils.py +10 -12
- edc_consent/consent_definition.py +4 -1
- edc_dashboard/view_mixins/edc_view_mixin.py +1 -1
- edc_model_admin/mixins/model_admin_form_instructions_mixin.py +9 -3
- edc_model_admin/mixins/model_admin_next_url_redirect_mixin.py +3 -3
- edc_model_admin/mixins/model_admin_protect_pii_mixin.py +7 -7
- edc_model_admin/mixins/model_admin_redirect_on_delete_mixin.py +6 -8
- edc_pharmacy/admin_mixin.py +1 -1
- edc_pharmacy/approve_prescription.py +10 -8
- edc_pharmacy/auth_objects.py +1 -1
- edc_pharmacy/exceptions.py +7 -7
- edc_pharmacy/settings.py +1 -1
- edc_pharmacy/views/add_to_storage_bin_view.py +22 -24
- edc_pharmacy/views/allocate_to_subject_view.py +10 -18
- edc_pharmacy/views/celery_task_status_view.py +1 -2
- edc_pharmacy/views/confirm_stock_from_instance_view.py +1 -1
- edc_pharmacy/views/confirm_stock_from_queryset_view.py +5 -8
- edc_pharmacy/views/dispense_view.py +1 -1
- edc_pharmacy/views/move_to_storage_bin_view.py +7 -3
- edc_pharmacy/views/prepare_and_review_stock_request_view.py +62 -70
- edc_pharmacy/views/print_labels_view.py +1 -1
- edc_pharmacy/views/transfer_stock_view.py +1 -1
- edc_prn/modelform_mixins.py +8 -9
- edc_prn/models.py +3 -1
- edc_prn/site_prn_forms.py +2 -2
- edc_prn/templatetags/edc_prn_extras.py +1 -1
- edc_protocol/middleware.py +1 -1
- edc_protocol/research_protocol_config.py +7 -5
- edc_protocol_incident/admin/protocol_deviation_violation_admin.py +1 -1
- edc_protocol_incident/form_validators/mixins.py +9 -6
- edc_protocol_incident/modeladmin_mixins.py +1 -1
- edc_pylabels/admin/label_configuration_admin.py +1 -1
- edc_pylabels/admin/label_specification_admin.py +1 -1
- edc_pylabels/auth_objects.py +1 -1
- edc_pylabels/site_label_configs.py +2 -2
- edc_qareports/admin/qa_report_log_admin.py +5 -5
- edc_qareports/admin/qa_report_log_summary_admin.py +1 -1
- edc_qareports/forms/note_form.py +2 -3
- edc_qareports/modeladmin_mixins/list_filters.py +23 -19
- edc_qareports/modeladmin_mixins/note_modeladmin_mixin.py +7 -7
- edc_qareports/modeladmin_mixins/on_study_missing_values_modeladmin_mixin.py +8 -8
- edc_qareports/modeladmin_mixins/qa_report_modeladmin_mixin.py +3 -7
- edc_qareports/sql_generator/crf_case.py +1 -1
- edc_qareports/sql_generator/crf_subquery.py +1 -1
- edc_qareports/sql_generator/requisition_subquery.py +1 -1
- edc_qareports/sql_generator/sql_view_generator.py +1 -1
- edc_qareports/sql_generator/subquery_from_dict.py +39 -39
- edc_qareports/utils.py +12 -13
- edc_randomization/model_mixins.py +1 -1
- edc_randomization/system_checks.py +1 -1
- edc_refusal/admin.py +4 -2
- edc_registration/modeladmin_mixins.py +18 -20
- edc_registration/modelform_mixins.py +2 -2
- edc_registration/models/signals.py +1 -1
- edc_registration/utils.py +1 -1
- edc_reportable/age_evaluator.py +4 -4
- edc_reportable/data/grading_data/daids_july_2017.py +3 -3
- edc_reportable/evaluator.py +15 -15
- edc_reportable/exceptions.py +4 -4
- edc_reportable/forms/reportables_form_validator_mixin.py +6 -2
- edc_reportable/management/commands/export_reportables.py +1 -1
- edc_reportable/models/grading_exception.py +1 -6
- edc_reportable/models/normal_data.py +3 -3
- edc_reportable/models/reference_range_collection.py +1 -6
- edc_reportable/utils/get_grade_for_value.py +15 -15
- edc_reportable/utils/get_normal_data_or_raise.py +14 -14
- edc_reportable/utils/in_normal_bounds_or_raise.py +6 -6
- edc_reportable/utils/load_data.py +1 -1
- edc_reportable/utils/update_grading_exceptions.py +5 -5
- {clinicedc-2.0.28.dist-info → clinicedc-2.0.30.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.28.dist-info → clinicedc-2.0.30.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from uuid import uuid4
|
|
2
2
|
|
|
3
|
-
import pandas as pd
|
|
4
3
|
from celery.states import PENDING
|
|
5
4
|
from django.apps import apps as django_apps
|
|
6
5
|
from django.conf import settings
|
|
@@ -9,7 +8,6 @@ from django.contrib.auth.decorators import login_required
|
|
|
9
8
|
from django.http import HttpResponseRedirect
|
|
10
9
|
from django.urls import reverse
|
|
11
10
|
from django.utils.decorators import method_decorator
|
|
12
|
-
from django.utils.html import format_html
|
|
13
11
|
from django.utils.safestring import mark_safe
|
|
14
12
|
from django.views.generic import TemplateView
|
|
15
13
|
from django_pandas.io import read_frame
|
|
@@ -37,22 +35,24 @@ class PrepareAndReviewStockRequestView(
|
|
|
37
35
|
df = get_next_scheduled_visit_for_subjects_df(stock_request)
|
|
38
36
|
|
|
39
37
|
# get unallocated stock that appears in a stock request for this location
|
|
40
|
-
df_unallocated_request_items =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
df_unallocated_request_items = (
|
|
39
|
+
read_frame(
|
|
40
|
+
StockRequestItem.objects.values(
|
|
41
|
+
"stock_request__request_identifier",
|
|
42
|
+
"registered_subject__subject_identifier",
|
|
43
|
+
).filter(
|
|
44
|
+
stock_request__location=stock_request.location, allocation__isnull=True
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
.rename(
|
|
48
|
+
columns={
|
|
49
|
+
"registered_subject__subject_identifier": "subject_identifier",
|
|
50
|
+
"stock_request__request_identifier": "request_identifier",
|
|
51
|
+
},
|
|
52
|
+
)
|
|
53
|
+
.reset_index(drop=True)
|
|
52
54
|
)
|
|
53
|
-
df_unallocated_request_items.
|
|
54
|
-
df_unallocated_request_items = pd.merge(
|
|
55
|
-
df_unallocated_request_items,
|
|
55
|
+
df_unallocated_request_items = df_unallocated_request_items.merge(
|
|
56
56
|
df[["subject_identifier", "next_visit_code", "next_appt_datetime"]],
|
|
57
57
|
on="subject_identifier",
|
|
58
58
|
how="left",
|
|
@@ -60,13 +60,14 @@ class PrepareAndReviewStockRequestView(
|
|
|
60
60
|
df_unallocated_request_items = df_unallocated_request_items[
|
|
61
61
|
df_unallocated_request_items.next_visit_code.notna()
|
|
62
62
|
]
|
|
63
|
-
df_unallocated_request_items.sort_values(
|
|
64
|
-
|
|
65
|
-
)
|
|
63
|
+
df_unallocated_request_items = df_unallocated_request_items.sort_values(
|
|
64
|
+
by=["subject_identifier"]
|
|
65
|
+
).reset_index()
|
|
66
66
|
|
|
67
67
|
# exclude unallocated subjects from appts
|
|
68
|
-
df = df[
|
|
69
|
-
|
|
68
|
+
df = df[
|
|
69
|
+
~df.subject_identifier.isin(df_unallocated_request_items.subject_identifier)
|
|
70
|
+
].reset_index(drop=True)
|
|
70
71
|
|
|
71
72
|
kwargs.update(
|
|
72
73
|
stock_request=stock_request,
|
|
@@ -128,55 +129,46 @@ class PrepareAndReviewStockRequestView(
|
|
|
128
129
|
subjects_excluded_by_request=len(
|
|
129
130
|
df_unallocated_request_items.subject_identifier.unique()
|
|
130
131
|
),
|
|
131
|
-
nostock_table=
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
table_id="my_table",
|
|
144
|
-
)
|
|
145
|
-
), # nosec B703 B308
|
|
132
|
+
nostock_table=mark_safe( # noqa: S308
|
|
133
|
+
df_nostock.to_html(
|
|
134
|
+
columns=[
|
|
135
|
+
"subject_identifier",
|
|
136
|
+
"next_visit_code",
|
|
137
|
+
"next_appt_datetime",
|
|
138
|
+
],
|
|
139
|
+
index=True,
|
|
140
|
+
border=0,
|
|
141
|
+
classes="table table-striped",
|
|
142
|
+
table_id="my_table",
|
|
143
|
+
)
|
|
146
144
|
),
|
|
147
|
-
instock_table=
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
table_id="in_stock_table",
|
|
161
|
-
)
|
|
162
|
-
), # nosec B703 B308
|
|
145
|
+
instock_table=mark_safe( # noqa: S308
|
|
146
|
+
df_instock.to_html(
|
|
147
|
+
columns=[
|
|
148
|
+
"subject_identifier",
|
|
149
|
+
"next_visit_code",
|
|
150
|
+
"next_appt_datetime",
|
|
151
|
+
"code",
|
|
152
|
+
],
|
|
153
|
+
index=True,
|
|
154
|
+
border=0,
|
|
155
|
+
classes="table table-striped",
|
|
156
|
+
table_id="in_stock_table",
|
|
157
|
+
)
|
|
163
158
|
),
|
|
164
|
-
unallocated_table=
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
table_id="unallocated_table",
|
|
178
|
-
)
|
|
179
|
-
), # nosec B703 B308
|
|
159
|
+
unallocated_table=mark_safe( # noqa: S308
|
|
160
|
+
df_unallocated_request_items.to_html(
|
|
161
|
+
columns=[
|
|
162
|
+
"subject_identifier",
|
|
163
|
+
"next_visit_code",
|
|
164
|
+
"next_appt_datetime",
|
|
165
|
+
"request_identifier",
|
|
166
|
+
],
|
|
167
|
+
index=True,
|
|
168
|
+
border=0,
|
|
169
|
+
classes="table table-striped",
|
|
170
|
+
table_id="unallocated_table",
|
|
171
|
+
)
|
|
180
172
|
),
|
|
181
173
|
session_uuid=session_uuid,
|
|
182
174
|
)
|
|
@@ -190,7 +182,7 @@ class PrepareAndReviewStockRequestView(
|
|
|
190
182
|
def model_cls(self):
|
|
191
183
|
return django_apps.get_model("edc_pharmacy.stocktransfer")
|
|
192
184
|
|
|
193
|
-
def post(self, request, *args, **kwargs):
|
|
185
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
194
186
|
session_uuid = request.POST.get("session_uuid")
|
|
195
187
|
stock_request = StockRequest.objects.get(pk=request.POST.get("stock_request"))
|
|
196
188
|
if not request.POST.get("cancel") and session_uuid:
|
|
@@ -58,7 +58,7 @@ class PrintLabelsView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, Templ
|
|
|
58
58
|
def model_cls(self):
|
|
59
59
|
return django_apps.get_model(f"edc_pharmacy.{self.kwargs.get('model')}")
|
|
60
60
|
|
|
61
|
-
def post(self, request, *args, **kwargs):
|
|
61
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
62
62
|
session_uuid = str(kwargs.get("session_uuid"))
|
|
63
63
|
stock_pks = request.session.get(session_uuid, [])
|
|
64
64
|
url = self.source_changelist_url
|
|
@@ -54,7 +54,7 @@ class TransferStockView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, Tem
|
|
|
54
54
|
def model_cls(self):
|
|
55
55
|
return django_apps.get_model("edc_pharmacy.stocktransfer")
|
|
56
56
|
|
|
57
|
-
def post(self, request, *args, **kwargs):
|
|
57
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
58
58
|
stock_transfer = StockTransfer.objects.get(pk=self.kwargs.get("stock_transfer"))
|
|
59
59
|
stock_codes = request.POST.getlist("codes")
|
|
60
60
|
if stock_codes:
|
edc_prn/modelform_mixins.py
CHANGED
|
@@ -8,7 +8,6 @@ from django.utils.text import format_lazy
|
|
|
8
8
|
from django.utils.translation import gettext_lazy as _
|
|
9
9
|
|
|
10
10
|
from edc_consent import site_consents
|
|
11
|
-
from edc_consent.consent_definition import ConsentDefinition
|
|
12
11
|
from edc_crf.crf_form_validator_mixins import BaseFormValidatorMixin
|
|
13
12
|
|
|
14
13
|
|
|
@@ -19,17 +18,17 @@ class PrnFormValidatorMixin(BaseFormValidatorMixin):
|
|
|
19
18
|
|
|
20
19
|
@property
|
|
21
20
|
def subject_consent(self):
|
|
22
|
-
return
|
|
21
|
+
return site_consents.get_consent_definition(
|
|
23
22
|
report_datetime=self.report_datetime
|
|
24
23
|
).model_cls.objects.get(subject_identifier=self.subject_identifier)
|
|
25
24
|
|
|
26
|
-
def get_consent_definition(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
) -> ConsentDefinition:
|
|
32
|
-
|
|
25
|
+
# def get_consent_definition(
|
|
26
|
+
# self,
|
|
27
|
+
# report_datetime: datetime | None = None,
|
|
28
|
+
# fldname: str | None = None,
|
|
29
|
+
# error_code: str | None = None,
|
|
30
|
+
# ) -> ConsentDefinition:
|
|
31
|
+
# return site_consents.get_consent_definition(report_datetime=self.report_datetime)
|
|
33
32
|
|
|
34
33
|
@property
|
|
35
34
|
def report_datetime(self) -> datetime:
|
edc_prn/models.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
|
|
5
|
+
from edc_constants.constants import NULL_STRING
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class SingletonPrnModelMixin(models.Model):
|
|
7
9
|
"""Enforces one record per subject."""
|
|
@@ -11,7 +13,7 @@ class SingletonPrnModelMixin(models.Model):
|
|
|
11
13
|
max_length=50,
|
|
12
14
|
unique=True,
|
|
13
15
|
help_text="auto updated for unique constraint",
|
|
14
|
-
|
|
16
|
+
default=NULL_STRING,
|
|
15
17
|
editable=False,
|
|
16
18
|
)
|
|
17
19
|
|
edc_prn/site_prn_forms.py
CHANGED
|
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from edc_prn import Prn
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class AlreadyRegistered(Exception):
|
|
17
|
+
class AlreadyRegistered(Exception): # noqa: N818
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
20
|
|
|
@@ -72,7 +72,7 @@ class PrnFormsCollection:
|
|
|
72
72
|
except ImportError as e:
|
|
73
73
|
site_prn_forms.registry = before_import_registry
|
|
74
74
|
if module_has_submodule(mod, module_name):
|
|
75
|
-
raise SitePrnFormsError(str(e))
|
|
75
|
+
raise SitePrnFormsError(str(e)) from e
|
|
76
76
|
except ImportError:
|
|
77
77
|
pass
|
|
78
78
|
|
|
@@ -23,7 +23,7 @@ def prn_list_items(subject_identifier, **kwargs):
|
|
|
23
23
|
prn_forms = []
|
|
24
24
|
for prn in site_prn_forms:
|
|
25
25
|
if prn.get_show_on_dashboard(subject_identifier=subject_identifier):
|
|
26
|
-
prn_forms.append(prn)
|
|
26
|
+
prn_forms.append(prn) # noqa: PERF401
|
|
27
27
|
if prn_forms:
|
|
28
28
|
prn_forms = sorted(prn_forms, key=lambda x: x.verbose_name)
|
|
29
29
|
return dict(prn_forms=prn_forms, subject_identifier=subject_identifier)
|
edc_protocol/middleware.py
CHANGED
|
@@ -11,7 +11,7 @@ class ResearchProtocolConfigMiddleware:
|
|
|
11
11
|
def process_view(self, request, *args):
|
|
12
12
|
pass
|
|
13
13
|
|
|
14
|
-
def process_template_response(self, request, response):
|
|
14
|
+
def process_template_response(self, request, response): # noqa: ARG002
|
|
15
15
|
if not response.context_data:
|
|
16
16
|
response.context_data = {}
|
|
17
17
|
protocol_config = ResearchProtocolConfig()
|
|
@@ -134,7 +134,9 @@ class ResearchProtocolConfig:
|
|
|
134
134
|
return getattr(
|
|
135
135
|
settings,
|
|
136
136
|
"EDC_PROTOCOL_SUBJECT_IDENTIFIER_PATTERN",
|
|
137
|
-
r"
|
|
137
|
+
r"{protocol_number}\-[0-9\-]+".format(
|
|
138
|
+
**dict(protocol_number=self.protocol_number)
|
|
139
|
+
),
|
|
138
140
|
)
|
|
139
141
|
|
|
140
142
|
@property
|
|
@@ -145,14 +147,14 @@ class ResearchProtocolConfig:
|
|
|
145
147
|
def study_open_datetime(self) -> datetime:
|
|
146
148
|
try:
|
|
147
149
|
study_open_datetime = settings.EDC_PROTOCOL_STUDY_OPEN_DATETIME
|
|
148
|
-
except AttributeError:
|
|
150
|
+
except AttributeError as e:
|
|
149
151
|
raise ImproperlyConfigured(
|
|
150
152
|
self.error_msg1
|
|
151
153
|
% {
|
|
152
154
|
"attr": "study_open_datetime",
|
|
153
155
|
"settings_attr": "EDC_PROTOCOL_STUDY_OPEN_DATETIME",
|
|
154
156
|
}
|
|
155
|
-
)
|
|
157
|
+
) from e
|
|
156
158
|
if not study_open_datetime:
|
|
157
159
|
raise ImproperlyConfigured(
|
|
158
160
|
self.error_msg2
|
|
@@ -167,14 +169,14 @@ class ResearchProtocolConfig:
|
|
|
167
169
|
def study_close_datetime(self) -> datetime:
|
|
168
170
|
try:
|
|
169
171
|
study_close_datetime = settings.EDC_PROTOCOL_STUDY_CLOSE_DATETIME
|
|
170
|
-
except AttributeError:
|
|
172
|
+
except AttributeError as e:
|
|
171
173
|
raise ImproperlyConfigured(
|
|
172
174
|
self.error_msg1
|
|
173
175
|
% {
|
|
174
176
|
"attr": "study_close_datetime",
|
|
175
177
|
"settings_attr": "EDC_PROTOCOL_STUDY_CLOSE_DATETIME",
|
|
176
178
|
}
|
|
177
|
-
)
|
|
179
|
+
) from e
|
|
178
180
|
if not study_close_datetime:
|
|
179
181
|
raise ImproperlyConfigured(
|
|
180
182
|
self.error_msg2
|
|
@@ -65,7 +65,7 @@ class ProtocolDeviationViolationAdmin(ModelAdminSubjectDashboardMixin, SimpleHis
|
|
|
65
65
|
audit_fieldset_tuple,
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
-
radio_fields = {
|
|
68
|
+
radio_fields = { # noqa: RUF012
|
|
69
69
|
"action_required": admin.VERTICAL,
|
|
70
70
|
"report_status": admin.VERTICAL,
|
|
71
71
|
"report_type": admin.VERTICAL,
|
|
@@ -40,9 +40,12 @@ class IncidentFormvalidatorMixin:
|
|
|
40
40
|
self.validate_date_not_before_incident("report_closed_datetime")
|
|
41
41
|
|
|
42
42
|
def validate_date_not_before_incident(self, fld_name):
|
|
43
|
-
if
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
if (
|
|
44
|
+
self.cleaned_data.get(fld_name)
|
|
45
|
+
and self.cleaned_data.get("incident_datetime")
|
|
46
|
+
and self.cleaned_data.get(fld_name) < self.cleaned_data.get("incident_datetime")
|
|
47
|
+
):
|
|
48
|
+
self.raise_validation_error(
|
|
49
|
+
{fld_name: "May not be before incident date/time"},
|
|
50
|
+
error_code=INVALID_ERROR,
|
|
51
|
+
)
|
|
@@ -26,7 +26,7 @@ class LabelSpecificationAdmin(
|
|
|
26
26
|
ModelAdminRedirectOnDeleteMixin,
|
|
27
27
|
admin.ModelAdmin,
|
|
28
28
|
):
|
|
29
|
-
actions =
|
|
29
|
+
actions = (copy_label_specification, export_to_csv)
|
|
30
30
|
|
|
31
31
|
instructions = (
|
|
32
32
|
"This model captures the dimensions, rows, columns, "
|
edc_pylabels/auth_objects.py
CHANGED
|
@@ -11,5 +11,5 @@ for app_config in django_apps.get_app_configs():
|
|
|
11
11
|
for model_cls in app_config.get_models():
|
|
12
12
|
app_name, model_name = model_cls._meta.label_lower.split(".")
|
|
13
13
|
for prefix in ["add", "change", "view", "delete"]:
|
|
14
|
-
codenames.append(f"{app_name}.{prefix}_{model_name}")
|
|
14
|
+
codenames.append(f"{app_name}.{prefix}_{model_name}") # noqa: PERF401
|
|
15
15
|
codenames.sort()
|
|
@@ -11,7 +11,7 @@ from django.core.management.color import color_style
|
|
|
11
11
|
from django.utils.module_loading import module_has_submodule
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class AlreadyRegistered(Exception):
|
|
14
|
+
class AlreadyRegistered(Exception): # noqa: N818
|
|
15
15
|
pass
|
|
16
16
|
|
|
17
17
|
|
|
@@ -104,7 +104,7 @@ class SiteLabelConfigs:
|
|
|
104
104
|
except ImportError as e:
|
|
105
105
|
site_label_configs.registry = before_import_registry
|
|
106
106
|
if module_has_submodule(mod, module_name):
|
|
107
|
-
raise SitePharmacyError(str(e))
|
|
107
|
+
raise SitePharmacyError(str(e)) from e
|
|
108
108
|
except ImportError:
|
|
109
109
|
pass
|
|
110
110
|
|
|
@@ -17,15 +17,15 @@ class QaReportLogAdmin(
|
|
|
17
17
|
TemplatesModelAdminMixin,
|
|
18
18
|
admin.ModelAdmin,
|
|
19
19
|
):
|
|
20
|
-
ordering =
|
|
20
|
+
ordering = ("-accessed",)
|
|
21
21
|
|
|
22
|
-
list_display =
|
|
22
|
+
list_display = ("report", "username", "site", "accessed", "report_model")
|
|
23
23
|
|
|
24
|
-
list_filter =
|
|
24
|
+
list_filter = ("accessed", "report_model", "username", "site")
|
|
25
25
|
|
|
26
|
-
search_fields =
|
|
26
|
+
search_fields = ("report_model", "username")
|
|
27
27
|
|
|
28
|
-
readonly_fields =
|
|
28
|
+
readonly_fields = ("report_model", "username", "site", "accessed")
|
|
29
29
|
|
|
30
30
|
@admin.display(description="Report", ordering="report_model")
|
|
31
31
|
def report(self, obj=None):
|
edc_qareports/forms/note_form.py
CHANGED
|
@@ -18,15 +18,14 @@ class NoteForm(
|
|
|
18
18
|
FormValidatorMixin,
|
|
19
19
|
forms.ModelForm,
|
|
20
20
|
):
|
|
21
|
-
|
|
22
21
|
report_datetime_field_attr = "report_datetime"
|
|
23
22
|
form_validator_cls = NoteFormValidator
|
|
24
23
|
|
|
25
24
|
class Meta:
|
|
26
25
|
model = Note
|
|
27
26
|
fields = "__all__"
|
|
28
|
-
help_text = {"subject_identifier": "(read-only)", "name": "(read-only)"}
|
|
29
|
-
widgets = {
|
|
27
|
+
help_text = {"subject_identifier": "(read-only)", "name": "(read-only)"} # noqa: RUF012
|
|
28
|
+
widgets = { # noqa: RUF012
|
|
30
29
|
"report_model": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
31
30
|
"subject_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
32
31
|
"name": forms.TextInput(attrs={"readonly": "readonly"}),
|
|
@@ -17,7 +17,7 @@ class NoteStatusListFilter(SimpleListFilter):
|
|
|
17
17
|
self.note_model_cls = model_admin.note_model_cls
|
|
18
18
|
super().__init__(request, params, model, model_admin)
|
|
19
19
|
|
|
20
|
-
def lookups(self, request, model_admin):
|
|
20
|
+
def lookups(self, request, model_admin): # noqa: ARG002
|
|
21
21
|
status_dict = {tpl[0]: tpl[1] for tpl in self.note_model_status_choices}
|
|
22
22
|
names = [(NEW, status_dict.get(NEW, "New"))]
|
|
23
23
|
qs = (
|
|
@@ -27,7 +27,7 @@ class NoteStatusListFilter(SimpleListFilter):
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
for obj in qs:
|
|
30
|
-
names.append((f"{obj.get('status')}", status_dict[obj.get("status")]))
|
|
30
|
+
names.append((f"{obj.get('status')}", status_dict[obj.get("status")])) # noqa: PERF401
|
|
31
31
|
return tuple(names)
|
|
32
32
|
|
|
33
33
|
@staticmethod
|
|
@@ -39,22 +39,26 @@ class NoteStatusListFilter(SimpleListFilter):
|
|
|
39
39
|
)
|
|
40
40
|
for obj in qs:
|
|
41
41
|
return obj.get("report_model")
|
|
42
|
+
return ""
|
|
42
43
|
|
|
43
|
-
def queryset(self, request, queryset):
|
|
44
|
-
if
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
def queryset(self, request, queryset): # noqa: ARG002
|
|
45
|
+
if (
|
|
46
|
+
self.value()
|
|
47
|
+
and self.value() != "none"
|
|
48
|
+
and (report_model := self.report_model(queryset))
|
|
49
|
+
):
|
|
50
|
+
if self.value() == NEW:
|
|
51
|
+
qs = self.note_model_cls.objects.values("subject_identifier").filter(
|
|
52
|
+
report_model=report_model
|
|
53
|
+
)
|
|
54
|
+
queryset = queryset.exclude(
|
|
55
|
+
subject_identifier__in=[obj.get("subject_identifier") for obj in qs]
|
|
56
|
+
)
|
|
57
|
+
elif self.value() in [tpl[0] for tpl in self.note_model_status_choices]:
|
|
58
|
+
qs = self.note_model_cls.objects.values("subject_identifier").filter(
|
|
59
|
+
report_model=report_model, status=self.value()
|
|
60
|
+
)
|
|
61
|
+
queryset = queryset.filter(
|
|
62
|
+
subject_identifier__in=[obj.get("subject_identifier") for obj in qs]
|
|
63
|
+
)
|
|
60
64
|
return queryset
|
|
@@ -32,7 +32,7 @@ class NoteModelAdminMixin(
|
|
|
32
32
|
"""A modeladmin mixin class for the Note model."""
|
|
33
33
|
|
|
34
34
|
form = NoteForm
|
|
35
|
-
ordering =
|
|
35
|
+
ordering = ("site", "subject_identifier")
|
|
36
36
|
|
|
37
37
|
note_template_name = "edc_qareports/qa_report_note.html"
|
|
38
38
|
|
|
@@ -52,26 +52,26 @@ class NoteModelAdminMixin(
|
|
|
52
52
|
audit_fieldset_tuple,
|
|
53
53
|
)
|
|
54
54
|
|
|
55
|
-
list_display =
|
|
55
|
+
list_display = (
|
|
56
56
|
"dashboard",
|
|
57
57
|
"subject_identifier",
|
|
58
58
|
"report",
|
|
59
59
|
"status",
|
|
60
60
|
"report_note",
|
|
61
61
|
"report_datetime",
|
|
62
|
-
|
|
62
|
+
)
|
|
63
63
|
|
|
64
|
-
radio_fields = {"status": admin.VERTICAL}
|
|
64
|
+
radio_fields = {"status": admin.VERTICAL} # noqa: RUF012
|
|
65
65
|
|
|
66
|
-
list_filter =
|
|
66
|
+
list_filter = (
|
|
67
67
|
"report_datetime",
|
|
68
68
|
"status",
|
|
69
69
|
"report_model",
|
|
70
70
|
"user_created",
|
|
71
71
|
"user_modified",
|
|
72
|
-
|
|
72
|
+
)
|
|
73
73
|
|
|
74
|
-
search_fields =
|
|
74
|
+
search_fields = ("subject_identifier", "name")
|
|
75
75
|
|
|
76
76
|
@admin.display(description="Report", ordering="report_name")
|
|
77
77
|
def report(self, obj=None):
|
|
@@ -23,9 +23,9 @@ class OnStudyMissingValuesModelAdminMixin(
|
|
|
23
23
|
include_note_column: bool = True
|
|
24
24
|
site_list_display_insert_pos: int = 2
|
|
25
25
|
qa_report_list_display_insert_pos = 4
|
|
26
|
-
ordering =
|
|
26
|
+
ordering = ("site", "subject_identifier")
|
|
27
27
|
|
|
28
|
-
list_display =
|
|
28
|
+
list_display = (
|
|
29
29
|
"dashboard",
|
|
30
30
|
"render_button",
|
|
31
31
|
"subject",
|
|
@@ -35,16 +35,16 @@ class OnStudyMissingValuesModelAdminMixin(
|
|
|
35
35
|
"visit",
|
|
36
36
|
"report_date",
|
|
37
37
|
"created",
|
|
38
|
-
|
|
38
|
+
)
|
|
39
39
|
|
|
40
|
-
list_filter =
|
|
40
|
+
list_filter = (
|
|
41
41
|
ScheduleStatusListFilter,
|
|
42
42
|
"label",
|
|
43
43
|
"visit_code",
|
|
44
44
|
"report_datetime",
|
|
45
|
-
|
|
45
|
+
)
|
|
46
46
|
|
|
47
|
-
search_fields =
|
|
47
|
+
search_fields = ("subject_identifier", "label")
|
|
48
48
|
|
|
49
49
|
@admin.display(description="Update")
|
|
50
50
|
def render_button(self, obj=None):
|
|
@@ -64,7 +64,7 @@ class OnStudyMissingValuesModelAdminMixin(
|
|
|
64
64
|
f"&appointment={self.related_visit(obj).appointment.id}"
|
|
65
65
|
f"&requisition={obj.original_id}"
|
|
66
66
|
)
|
|
67
|
-
title = _(
|
|
67
|
+
title = _("Add {}") % crf_model_cls._meta.verbose_name
|
|
68
68
|
label = _("Add CRF")
|
|
69
69
|
crf_button = render_to_string(
|
|
70
70
|
"edc_qareports/columns/add_button.html",
|
|
@@ -80,7 +80,7 @@ class OnStudyMissingValuesModelAdminMixin(
|
|
|
80
80
|
f"{url}?next={self.admin_site.name}:"
|
|
81
81
|
f"{self.model._meta.label_lower.replace('.', '_')}_changelist"
|
|
82
82
|
)
|
|
83
|
-
title = _(
|
|
83
|
+
title = _("Change {}") % crf_model_cls._meta.verbose_name
|
|
84
84
|
label = _("Change CRF")
|
|
85
85
|
crf_button = render_to_string(
|
|
86
86
|
"edc_qareports/columns/change_button.html",
|
|
@@ -62,13 +62,9 @@ class QaReportModelAdminMixin:
|
|
|
62
62
|
return tuple(list_filter)
|
|
63
63
|
|
|
64
64
|
def get_note_model_obj_or_raise(self, obj=None):
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
except ObjectDoesNotExist:
|
|
70
|
-
raise
|
|
71
|
-
return note_model_obj
|
|
65
|
+
return self.note_model_cls.objects.get(
|
|
66
|
+
report_model=obj.report_model, subject_identifier=obj.subject_identifier
|
|
67
|
+
)
|
|
72
68
|
|
|
73
69
|
@admin.display(description="Status")
|
|
74
70
|
def status(self, obj) -> str:
|