clinicedc 2.0.27__py3-none-any.whl → 2.0.29__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.27.dist-info → clinicedc-2.0.29.dist-info}/METADATA +2 -2
- {clinicedc-2.0.27.dist-info → clinicedc-2.0.29.dist-info}/RECORD +100 -100
- 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 +9 -11
- edc_consent/consent_definition.py +4 -1
- edc_consent/models/__init__.py +2 -2
- edc_consent/models/signals.py +32 -26
- edc_model_admin/mixins/model_admin_form_instructions_mixin.py +9 -3
- edc_model_admin/mixins/model_admin_protect_pii_mixin.py +7 -7
- 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/randomization_list_importer.py +10 -4
- edc_randomization/randomization_list_verifier.py +5 -3
- 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
- edc_visit_schedule/site_visit_schedules.py +8 -12
- {clinicedc-2.0.27.dist-info → clinicedc-2.0.29.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.27.dist-info → clinicedc-2.0.29.dist-info}/licenses/LICENSE +0 -0
edc_auth/utils.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
4
|
+
import sys
|
|
3
5
|
from pprint import pprint
|
|
4
6
|
from typing import TYPE_CHECKING, Any
|
|
5
7
|
|
|
@@ -24,25 +26,23 @@ def get_user(username: str) -> User | None:
|
|
|
24
26
|
return user
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
def compare_codenames_for_group(group_name: str
|
|
29
|
+
def compare_codenames_for_group(group_name: str, expected: list[str]) -> None:
|
|
28
30
|
group = django_apps.get_model("auth.group").objects.get(name=group_name)
|
|
29
31
|
codenames = [p.codename for p in group.permissions.all()]
|
|
30
32
|
new_expected = []
|
|
31
33
|
for c in expected:
|
|
32
|
-
|
|
34
|
+
with contextlib.suppress(IndexError):
|
|
33
35
|
c = c.split(".")[1]
|
|
34
|
-
except IndexError:
|
|
35
|
-
pass
|
|
36
36
|
new_expected.append(c)
|
|
37
37
|
|
|
38
38
|
compared = [c for c in new_expected if c not in codenames]
|
|
39
39
|
if compared:
|
|
40
|
-
|
|
41
|
-
pprint(compared)
|
|
40
|
+
sys.stdout.write(f"{group.name}, missing from codenames\n")
|
|
41
|
+
pprint(compared) # noqa: T203
|
|
42
42
|
compared = [c for c in codenames if c not in new_expected]
|
|
43
43
|
if compared:
|
|
44
|
-
|
|
45
|
-
pprint(compared)
|
|
44
|
+
sys.stdout.write(f"{group.name}, extra codenames\n")
|
|
45
|
+
pprint(compared) # noqa: T203
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def remove_default_model_permissions_from_edc_permissions(auth_updater: Any, app_label: str):
|
|
@@ -119,9 +119,7 @@ def get_codenames_for_user(
|
|
|
119
119
|
for role in roles.all():
|
|
120
120
|
groups.extend([grp for grp in role.groups.all() if grp not in account_manager_groups])
|
|
121
121
|
if include_groups:
|
|
122
|
-
for
|
|
123
|
-
if group not in account_manager_groups:
|
|
124
|
-
groups.append(group)
|
|
122
|
+
groups.extend([grp for grp in user.groups.all() if grp not in account_manager_groups])
|
|
125
123
|
groups = list(set(groups))
|
|
126
124
|
for group in groups:
|
|
127
125
|
codenames.extend(
|
|
@@ -101,7 +101,10 @@ class ConsentDefinition:
|
|
|
101
101
|
|
|
102
102
|
@property
|
|
103
103
|
def model(self):
|
|
104
|
-
from .managers import
|
|
104
|
+
from .managers import ( # noqa: PLC0415
|
|
105
|
+
ConsentObjectsByCdefManager,
|
|
106
|
+
CurrentSiteByCdefManager,
|
|
107
|
+
)
|
|
105
108
|
|
|
106
109
|
model_cls = django_apps.get_model(self._model)
|
|
107
110
|
if not model_cls._meta.proxy:
|
edc_consent/models/__init__.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from .edc_permissions import EdcPermissions
|
|
2
2
|
from .signals import (
|
|
3
3
|
requires_consent_on_pre_save,
|
|
4
|
-
|
|
4
|
+
update_appointment_from_consentext_post_save,
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"EdcPermissions",
|
|
9
9
|
"requires_consent_on_pre_save",
|
|
10
|
-
|
|
10
|
+
"update_appointment_from_consentext_post_save",
|
|
11
11
|
]
|
edc_consent/models/signals.py
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import contextlib
|
|
2
|
+
|
|
3
|
+
from django.db.models.signals import post_save, pre_save
|
|
2
4
|
from django.dispatch import receiver
|
|
3
5
|
|
|
4
6
|
from edc_sites import site_sites
|
|
7
|
+
from edc_visit_schedule.exceptions import NotOnScheduleError
|
|
8
|
+
from edc_visit_schedule.site_visit_schedules import site_visit_schedules
|
|
9
|
+
from edc_visit_schedule.subject_schedule import SubjectSchedule
|
|
5
10
|
|
|
6
|
-
from ..model_mixins import RequiresConsentFieldsModelMixin
|
|
11
|
+
from ..model_mixins import ConsentExtensionModelMixin, RequiresConsentFieldsModelMixin
|
|
7
12
|
from ..site_consents import site_consents
|
|
8
13
|
|
|
9
14
|
|
|
@@ -50,27 +55,28 @@ def requires_consent_on_pre_save(instance, raw, using, update_fields, **kwargs):
|
|
|
50
55
|
instance.consent_model = consent_definition.model
|
|
51
56
|
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
@receiver(
|
|
59
|
+
post_save,
|
|
60
|
+
weak=False,
|
|
61
|
+
dispatch_uid="update_appointment_from_consentext_post_save",
|
|
62
|
+
)
|
|
63
|
+
def update_appointment_from_consentext_post_save(
|
|
64
|
+
sender, instance, raw, created, using, **kwargs
|
|
65
|
+
):
|
|
66
|
+
if (
|
|
67
|
+
not raw
|
|
68
|
+
and not kwargs.get("update_fields")
|
|
69
|
+
and isinstance(instance, (ConsentExtensionModelMixin,))
|
|
70
|
+
):
|
|
71
|
+
cdef = site_consents.get_consent_definition(
|
|
72
|
+
model=instance.subject_consent._meta.label_lower,
|
|
73
|
+
version=instance.subject_consent.version,
|
|
74
|
+
)
|
|
75
|
+
for visit_schedule, schedule in site_visit_schedules.get_by_consent_definition(cdef):
|
|
76
|
+
subject_schedule = SubjectSchedule(
|
|
77
|
+
instance.subject_consent.subject_identifier,
|
|
78
|
+
visit_schedule=visit_schedule,
|
|
79
|
+
schedule=schedule,
|
|
80
|
+
)
|
|
81
|
+
with contextlib.suppress(NotOnScheduleError):
|
|
82
|
+
subject_schedule.refresh_appointments()
|
|
@@ -46,7 +46,9 @@ class ModelAdminFormInstructionsMixin:
|
|
|
46
46
|
change_additional_instructions = ()
|
|
47
47
|
|
|
48
48
|
def get_add_instructions(
|
|
49
|
-
self,
|
|
49
|
+
self,
|
|
50
|
+
extra_context: dict | None,
|
|
51
|
+
request: WSGIRequest | None = None, # noqa: ARG002
|
|
50
52
|
):
|
|
51
53
|
extra_context = extra_context or {}
|
|
52
54
|
extra_context["instructions"] = self.add_instructions or self.instructions
|
|
@@ -56,7 +58,9 @@ class ModelAdminFormInstructionsMixin:
|
|
|
56
58
|
return extra_context
|
|
57
59
|
|
|
58
60
|
def get_change_instructions(
|
|
59
|
-
self,
|
|
61
|
+
self,
|
|
62
|
+
extra_context: dict | None,
|
|
63
|
+
request: WSGIRequest | None = None, # noqa: ARG002
|
|
60
64
|
):
|
|
61
65
|
extra_context = extra_context or {}
|
|
62
66
|
extra_context["instructions"] = self.change_instructions or self.instructions
|
|
@@ -65,7 +69,9 @@ class ModelAdminFormInstructionsMixin:
|
|
|
65
69
|
)
|
|
66
70
|
return extra_context
|
|
67
71
|
|
|
68
|
-
def add_view(
|
|
72
|
+
def add_view(
|
|
73
|
+
self, request: WSGIRequest, form_url: str = "", extra_context: dict | None = None
|
|
74
|
+
):
|
|
69
75
|
extra_context = self.get_add_instructions(extra_context, request=request)
|
|
70
76
|
return super().add_view(request, form_url=form_url, extra_context=extra_context)
|
|
71
77
|
|
|
@@ -23,20 +23,20 @@ class ModelAdminProtectPiiMixin:
|
|
|
23
23
|
|
|
24
24
|
extra_pii_attrs: tuple[str] | None = ()
|
|
25
25
|
|
|
26
|
-
def get_extra_pii_attrs(self) ->
|
|
27
|
-
return self.extra_pii_attrs
|
|
26
|
+
def get_extra_pii_attrs(self) -> tuple[str | tuple[str, str]]:
|
|
27
|
+
return self.extra_pii_attrs
|
|
28
28
|
|
|
29
29
|
def get_encrypted_fields(self) -> tuple[str, ...]:
|
|
30
|
-
encrypted_fields = [f.name for f in get_encrypted_fields(self.model)]
|
|
31
|
-
|
|
32
|
-
return tuple(set(encrypted_fields))
|
|
30
|
+
encrypted_fields = tuple([f.name for f in get_encrypted_fields(self.model)])
|
|
31
|
+
return tuple({*encrypted_fields, *self.get_extra_pii_attrs()})
|
|
33
32
|
|
|
34
33
|
def get_list_display(self, request) -> tuple[str]:
|
|
35
34
|
list_display = super().get_list_display(request)
|
|
36
35
|
if not request.user.groups.filter(name__in=[PII, PII_VIEW]).exists():
|
|
37
36
|
# TODO: search replace from list_display if extra_pii_attr has tuple
|
|
38
|
-
list_display =
|
|
39
|
-
|
|
37
|
+
list_display = tuple(
|
|
38
|
+
*{f for f in list_display if f not in self.get_encrypted_fields()}
|
|
39
|
+
)
|
|
40
40
|
return list_display
|
|
41
41
|
|
|
42
42
|
def get_list_display_links(self, request, list_display) -> list[str] | None:
|
edc_pharmacy/admin_mixin.py
CHANGED
|
@@ -7,7 +7,7 @@ class PrintButtonAdminMixin:
|
|
|
7
7
|
print_server_error = None
|
|
8
8
|
|
|
9
9
|
def __init__(self, args, *kwargs):
|
|
10
|
-
super(
|
|
10
|
+
super().__init__(*args, **kwargs)
|
|
11
11
|
app_config = django_apps.get_app_config("edc_label")
|
|
12
12
|
self._print_server = None
|
|
13
13
|
self._printers = {}
|
|
@@ -8,12 +8,14 @@ class ApprovePrescription:
|
|
|
8
8
|
def __init__(self, rx_model_obj=None):
|
|
9
9
|
dispense_appointment = rx_model_obj.dispense_appointment
|
|
10
10
|
previous_appointment = dispense_appointment.previous()
|
|
11
|
-
if
|
|
12
|
-
|
|
11
|
+
if (
|
|
12
|
+
previous_appointment
|
|
13
|
+
and rx_model_obj.__class__.objects.filter(
|
|
13
14
|
is_approved=False, dispense_appointment=previous_appointment
|
|
14
|
-
).exists()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
).exists()
|
|
16
|
+
):
|
|
17
|
+
raise PrescriptionApprovalValidatorError(
|
|
18
|
+
f"Future prescriptions cannot be approved. Approve "
|
|
19
|
+
f"prescriptions for dispensing date "
|
|
20
|
+
f"{previous_appointment.appt_datetime}"
|
|
21
|
+
)
|
edc_pharmacy/auth_objects.py
CHANGED
|
@@ -20,7 +20,7 @@ PHARMACY_SUPER_ROLE = "PHARMACY_SUPER_ROLE"
|
|
|
20
20
|
navbar_codenames = ["edc_pharmacy.nav_pharmacy_section"]
|
|
21
21
|
navbar_tuples = []
|
|
22
22
|
for codename in navbar_codenames:
|
|
23
|
-
navbar_tuples.append((codename, f"Can access {codename.split('.')[1]}"))
|
|
23
|
+
navbar_tuples.append((codename, f"Can access {codename.split('.')[1]}")) # noqa: PERF401
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
# central pharmacist
|
edc_pharmacy/exceptions.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class ActivePrescriptionRefillOverlap(Exception):
|
|
1
|
+
class ActivePrescriptionRefillOverlap(Exception): # noqa: N818
|
|
2
2
|
pass
|
|
3
3
|
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ class RefillError(Exception):
|
|
|
6
6
|
pass
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class ActiveRefillAlreadyExists(Exception):
|
|
9
|
+
class ActiveRefillAlreadyExists(Exception): # noqa: N818
|
|
10
10
|
pass
|
|
11
11
|
|
|
12
12
|
|
|
@@ -34,7 +34,7 @@ class PackagingLotNumberMismatchError(Exception):
|
|
|
34
34
|
pass
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
class PrescriptionAlreadyExists(Exception):
|
|
37
|
+
class PrescriptionAlreadyExists(Exception): # noqa: N818
|
|
38
38
|
pass
|
|
39
39
|
|
|
40
40
|
|
|
@@ -42,15 +42,15 @@ class PrescriptionError(Exception):
|
|
|
42
42
|
pass
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
class PrescriptionExpired(Exception):
|
|
45
|
+
class PrescriptionExpired(Exception): # noqa: N818
|
|
46
46
|
pass
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
class RefillAlreadyExists(Exception):
|
|
49
|
+
class RefillAlreadyExists(Exception): # noqa: N818
|
|
50
50
|
pass
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
class PrescriptionNotStarted(Exception):
|
|
53
|
+
class PrescriptionNotStarted(Exception): # noqa: N818
|
|
54
54
|
pass
|
|
55
55
|
|
|
56
56
|
|
|
@@ -74,7 +74,7 @@ class InsufficientStockError(Exception):
|
|
|
74
74
|
pass
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
class InvalidContainer(Exception):
|
|
77
|
+
class InvalidContainer(Exception): # noqa: N818
|
|
78
78
|
pass
|
|
79
79
|
|
|
80
80
|
|
edc_pharmacy/settings.py
CHANGED
|
@@ -15,7 +15,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|
|
15
15
|
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
|
16
16
|
|
|
17
17
|
# SECURITY WARNING: keep the secret key used in production secret!
|
|
18
|
-
SECRET_KEY = "django-insecure-+#-*m&byi1w8=#e5nr))^kollb=k1b#rvjffo#+teji^qpm14o" # nosec B105
|
|
18
|
+
SECRET_KEY = "django-insecure-+#-*m&byi1w8=#e5nr))^kollb=k1b#rvjffo#+teji^qpm14o" # nosec B105 # noqa: S105
|
|
19
19
|
|
|
20
20
|
# SECURITY WARNING: don't run with debug turned on in production!
|
|
21
21
|
DEBUG = True
|
|
@@ -95,8 +95,7 @@ class AddToStorageBinView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, T
|
|
|
95
95
|
def storage_bin_changelist_url(self) -> str:
|
|
96
96
|
if self.storage_bin:
|
|
97
97
|
url = reverse("edc_pharmacy_admin:edc_pharmacy_storagebin_changelist")
|
|
98
|
-
|
|
99
|
-
return url
|
|
98
|
+
return f"{url}?q={self.storage_bin.bin_identifier}"
|
|
100
99
|
return "/"
|
|
101
100
|
|
|
102
101
|
def redirect_on_has_duplicates(
|
|
@@ -144,30 +143,29 @@ class AddToStorageBinView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, T
|
|
|
144
143
|
def redirect_on_invalid_subject_for_location(
|
|
145
144
|
self, stock_codes: list[str], storage_bin: StorageBin
|
|
146
145
|
) -> HttpResponseRedirect | None:
|
|
147
|
-
if stock_codes
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return HttpResponseRedirect(url)
|
|
146
|
+
if stock_codes and (
|
|
147
|
+
Stock.objects.filter(code__in=stock_codes)
|
|
148
|
+
.exclude(location=storage_bin.location)
|
|
149
|
+
.exists()
|
|
150
|
+
):
|
|
151
|
+
qs = Stock.objects.filter(code__in=stock_codes).exclude(
|
|
152
|
+
location=storage_bin.location
|
|
153
|
+
)
|
|
154
|
+
messages.add_message(
|
|
155
|
+
self.request,
|
|
156
|
+
messages.ERROR,
|
|
157
|
+
f"Stock not from this location. See {[obj.stock.code for obj in qs]}.",
|
|
158
|
+
)
|
|
159
|
+
url = reverse(
|
|
160
|
+
"edc_pharmacy:add_to_storage_bin_url",
|
|
161
|
+
kwargs={
|
|
162
|
+
"storage_bin": storage_bin.id,
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
return HttpResponseRedirect(url)
|
|
168
166
|
return None
|
|
169
167
|
|
|
170
|
-
def post(self, request, *args, **kwargs):
|
|
168
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
171
169
|
stock_codes = request.POST.getlist("codes") if request.POST.get("codes") else None
|
|
172
170
|
storage_bin = StorageBin.objects.get(id=kwargs.get("storage_bin"))
|
|
173
171
|
items_to_scan = request.POST.get("items_to_scan") or kwargs.get("items_to_scan")
|
|
@@ -96,8 +96,7 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
96
96
|
def stock_request_changelist_url(self) -> str:
|
|
97
97
|
if self.stock_request:
|
|
98
98
|
url = reverse("edc_pharmacy_admin:edc_pharmacy_stockrequest_changelist")
|
|
99
|
-
|
|
100
|
-
return url
|
|
99
|
+
return f"{url}?q={self.stock_request.request_identifier}"
|
|
101
100
|
return "/"
|
|
102
101
|
|
|
103
102
|
@staticmethod
|
|
@@ -120,14 +119,13 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
120
119
|
messages.ERROR,
|
|
121
120
|
"Nothing saved. Duplicate codes detected in list. Please try again.",
|
|
122
121
|
)
|
|
123
|
-
|
|
122
|
+
return reverse(
|
|
124
123
|
"edc_pharmacy:allocate_url",
|
|
125
124
|
kwargs={
|
|
126
125
|
"stock_request": stock_request.id,
|
|
127
126
|
"assignment": assignment.id,
|
|
128
127
|
},
|
|
129
128
|
)
|
|
130
|
-
return url
|
|
131
129
|
return None
|
|
132
130
|
|
|
133
131
|
def redirect_on_uncomfirmed_stock_codes(
|
|
@@ -153,14 +151,13 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
153
151
|
f"Got {uncomfirmed_codes}. "
|
|
154
152
|
),
|
|
155
153
|
)
|
|
156
|
-
|
|
154
|
+
return reverse(
|
|
157
155
|
"edc_pharmacy:allocate_url",
|
|
158
156
|
kwargs={
|
|
159
157
|
"stock_request": stock_request.id,
|
|
160
158
|
"assignment": assignment.id,
|
|
161
159
|
},
|
|
162
160
|
)
|
|
163
|
-
return url
|
|
164
161
|
return None
|
|
165
162
|
|
|
166
163
|
def redirect_on_invalid_stock_codes(
|
|
@@ -183,14 +180,13 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
183
180
|
messages.ERROR,
|
|
184
181
|
f"Nothing saved. Invalid codes detected. Got {invalid_codes}. ",
|
|
185
182
|
)
|
|
186
|
-
|
|
183
|
+
return reverse(
|
|
187
184
|
"edc_pharmacy:allocate_url",
|
|
188
185
|
kwargs={
|
|
189
186
|
"stock_request": stock_request.id,
|
|
190
187
|
"assignment": assignment.id,
|
|
191
188
|
},
|
|
192
189
|
)
|
|
193
|
-
return url
|
|
194
190
|
return None
|
|
195
191
|
|
|
196
192
|
def redirect_on_has_multiple_container_types(
|
|
@@ -211,14 +207,13 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
211
207
|
f"only. See Stock request {stock_request.request_identifier} "
|
|
212
208
|
),
|
|
213
209
|
)
|
|
214
|
-
|
|
210
|
+
return reverse(
|
|
215
211
|
"edc_pharmacy:allocate_url",
|
|
216
212
|
kwargs={
|
|
217
213
|
"stock_request": stock_request.id,
|
|
218
214
|
"assignment": assignment.id,
|
|
219
215
|
},
|
|
220
216
|
)
|
|
221
|
-
return url
|
|
222
217
|
return None
|
|
223
218
|
|
|
224
219
|
def redirect_on_stock_already_allocated(
|
|
@@ -234,20 +229,19 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
234
229
|
allocated_stock_codes = []
|
|
235
230
|
for stock in Stock.objects.filter(code__in=stock_codes):
|
|
236
231
|
if stock.allocation:
|
|
237
|
-
allocated_stock_codes.append(stock.code)
|
|
232
|
+
allocated_stock_codes.append(stock.code) # noqa: PERF401
|
|
238
233
|
messages.add_message(
|
|
239
234
|
self.request,
|
|
240
235
|
messages.ERROR,
|
|
241
236
|
f"Stock already allocated. Got {','.join(allocated_stock_codes)}.",
|
|
242
237
|
)
|
|
243
|
-
|
|
238
|
+
return reverse(
|
|
244
239
|
"edc_pharmacy:allocate_url",
|
|
245
240
|
kwargs={
|
|
246
241
|
"stock_request": stock_request.id,
|
|
247
242
|
"assignment": getattr(assignment, "id", None),
|
|
248
243
|
},
|
|
249
244
|
)
|
|
250
|
-
return url
|
|
251
245
|
return None
|
|
252
246
|
|
|
253
247
|
def redirect_on_all_allocated_for_assignment(
|
|
@@ -265,14 +259,13 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
265
259
|
)
|
|
266
260
|
% {"assignment": assignment.display_name.upper()},
|
|
267
261
|
)
|
|
268
|
-
|
|
262
|
+
return reverse(
|
|
269
263
|
"edc_pharmacy:allocate_url",
|
|
270
264
|
kwargs={
|
|
271
265
|
"stock_request": stock_request.id,
|
|
272
266
|
"assignment": getattr(assignment, "id", None),
|
|
273
267
|
},
|
|
274
268
|
)
|
|
275
|
-
return url
|
|
276
269
|
return None
|
|
277
270
|
|
|
278
271
|
def redirect_on_incorrect_stock_for_assignment(
|
|
@@ -292,14 +285,13 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
292
285
|
f"Expected `{assignment.display_name}` only. Check your work."
|
|
293
286
|
),
|
|
294
287
|
)
|
|
295
|
-
|
|
288
|
+
return reverse(
|
|
296
289
|
"edc_pharmacy:allocate_url",
|
|
297
290
|
kwargs={
|
|
298
291
|
"stock_request": stock_request.id,
|
|
299
292
|
"assignment": getattr(assignment, "id", None),
|
|
300
293
|
},
|
|
301
294
|
)
|
|
302
|
-
return url
|
|
303
295
|
return None
|
|
304
296
|
|
|
305
297
|
def get_counts(self, stock_request: StockRequest) -> tuple[int, int]:
|
|
@@ -315,7 +307,7 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
315
307
|
return remaining_count or 0, total_count
|
|
316
308
|
return 0, 0
|
|
317
309
|
|
|
318
|
-
def post(self, request, *args, **kwargs):
|
|
310
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
319
311
|
stock_codes = request.POST.getlist("codes") if request.POST.get("codes") else None
|
|
320
312
|
subject_identifiers = request.POST.get("subject_identifiers")
|
|
321
313
|
assignment_id = request.POST.get("assignment")
|
|
@@ -4,8 +4,7 @@ from django.views import View
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class CeleryTaskStatusView(View):
|
|
7
|
-
|
|
8
|
-
def get(self, request, *args, **kwargs):
|
|
7
|
+
def get(self, request, *args, **kwargs): # noqa: ARG002
|
|
9
8
|
task_id = request.GET.get("task_id")
|
|
10
9
|
response_data = {"task_id": task_id, "status": None}
|
|
11
10
|
try:
|
|
@@ -90,7 +90,7 @@ class ConfirmStockFromInstanceView(
|
|
|
90
90
|
def model_cls(self):
|
|
91
91
|
return django_apps.get_model(f"edc_pharmacy.{self.kwargs.get('model')}")
|
|
92
92
|
|
|
93
|
-
def post(self, request, *args, **kwargs):
|
|
93
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
94
94
|
dct = self.get_values_dict(**kwargs)
|
|
95
95
|
codes = request.POST.getlist("codes")
|
|
96
96
|
confirmed, already_confirmed, invalid = confirm_stock(
|
|
@@ -29,8 +29,8 @@ class ConfirmStockFromQuerySetView(
|
|
|
29
29
|
navbar_selected_item = "pharmacy"
|
|
30
30
|
codes_per_page = 12
|
|
31
31
|
|
|
32
|
-
def get_context_data(self, **kwargs):
|
|
33
|
-
|
|
32
|
+
def get_context_data(self, **kwargs): # noqa: ARG002
|
|
33
|
+
return dict(
|
|
34
34
|
CONFIRMED=CONFIRMED,
|
|
35
35
|
ALREADY_CONFIRMED=ALREADY_CONFIRMED,
|
|
36
36
|
INVALID=INVALID,
|
|
@@ -40,7 +40,6 @@ class ConfirmStockFromQuerySetView(
|
|
|
40
40
|
source_changelist_url=self.source_changelist_url,
|
|
41
41
|
**self.session_data,
|
|
42
42
|
)
|
|
43
|
-
return context
|
|
44
43
|
|
|
45
44
|
@property
|
|
46
45
|
def session_data(self):
|
|
@@ -58,10 +57,8 @@ class ConfirmStockFromQuerySetView(
|
|
|
58
57
|
[
|
|
59
58
|
x,
|
|
60
59
|
ALREADY_CONFIRMED,
|
|
61
|
-
_(
|
|
62
|
-
|
|
63
|
-
% {"transaction_word": transaction_word}
|
|
64
|
-
),
|
|
60
|
+
_("already %(transaction_word)s")
|
|
61
|
+
% {"transaction_word": transaction_word},
|
|
65
62
|
]
|
|
66
63
|
for x in session_obj.get("already_confirmed_codes") or []
|
|
67
64
|
]
|
|
@@ -117,7 +114,7 @@ class ConfirmStockFromQuerySetView(
|
|
|
117
114
|
label_lower = self.session_data.get("source_label_lower")
|
|
118
115
|
return django_apps.get_model(label_lower)
|
|
119
116
|
|
|
120
|
-
def post(self, request, *args, **kwargs):
|
|
117
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
121
118
|
stock_codes = request.POST.getlist("stock_codes")
|
|
122
119
|
if len(stock_codes) != len(list(set(stock_codes))):
|
|
123
120
|
messages.add_message(
|
|
@@ -85,7 +85,7 @@ class DispenseView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, Template
|
|
|
85
85
|
)
|
|
86
86
|
return confirmation_at_site
|
|
87
87
|
|
|
88
|
-
def post(self, request, *args, **kwargs):
|
|
88
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
89
89
|
location_id = request.POST.get("location_id")
|
|
90
90
|
location = Location.objects.get(pk=location_id)
|
|
91
91
|
formulation_id = request.POST.get("formulation_id")
|
|
@@ -11,6 +11,7 @@ from ..models import Stock, StorageBin, StorageBinItem
|
|
|
11
11
|
from .add_to_storage_bin_view import AddToStorageBinView, StorageBinError
|
|
12
12
|
|
|
13
13
|
p = inflect.engine()
|
|
14
|
+
MAX_STORAGE_BIN_CAPACITY = 50
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def move_to_bin(
|
|
@@ -25,8 +26,11 @@ def move_to_bin(
|
|
|
25
26
|
new_capacity = StorageBinItem.objects.filter(storage_bin=storage_bin).count() + len(
|
|
26
27
|
stock_codes
|
|
27
28
|
)
|
|
28
|
-
if new_capacity >
|
|
29
|
-
raise StorageBinError(
|
|
29
|
+
if new_capacity > MAX_STORAGE_BIN_CAPACITY:
|
|
30
|
+
raise StorageBinError(
|
|
31
|
+
f"Storage bin {storage_bin.name} capacity "
|
|
32
|
+
f"may not exceeded {MAX_STORAGE_BIN_CAPACITY}."
|
|
33
|
+
)
|
|
30
34
|
if new_capacity > storage_bin.capacity:
|
|
31
35
|
storage_bin.capacity = new_capacity
|
|
32
36
|
storage_bin.save()
|
|
@@ -89,7 +93,7 @@ class MoveToStorageBinView(AddToStorageBinView):
|
|
|
89
93
|
return HttpResponseRedirect(url)
|
|
90
94
|
return None
|
|
91
95
|
|
|
92
|
-
def post(self, request, *args, **kwargs):
|
|
96
|
+
def post(self, request, *args, **kwargs): # noqa: ARG002
|
|
93
97
|
stock_codes = request.POST.getlist("codes") if request.POST.get("codes") else None
|
|
94
98
|
storage_bin = StorageBin.objects.get(id=kwargs.get("storage_bin"))
|
|
95
99
|
items_to_scan = request.POST.get("items_to_scan") or kwargs.get("items_to_scan")
|