clinicedc 2.0.23__py3-none-any.whl → 2.0.25__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.23.dist-info → clinicedc-2.0.25.dist-info}/METADATA +1 -1
- {clinicedc-2.0.23.dist-info → clinicedc-2.0.25.dist-info}/RECORD +48 -48
- edc_action_item/site_action_items.py +2 -2
- edc_appointment/view_utils/appointment_button.py +1 -1
- edc_consent/modeladmin_mixins/consent_model_admin_mixin.py +14 -12
- edc_consent/models/__init__.py +2 -2
- edc_consent/models/signals.py +26 -28
- edc_dashboard/view_mixins/message_view_mixin.py +3 -4
- edc_identifier/admin.py +3 -2
- edc_identifier/identifier.py +5 -5
- edc_identifier/model_mixins.py +1 -1
- edc_identifier/models.py +7 -6
- edc_identifier/research_identifier.py +1 -1
- edc_identifier/short_identifier.py +1 -1
- edc_identifier/simple_identifier.py +15 -5
- edc_identifier/subject_identifier.py +2 -2
- edc_identifier/utils.py +13 -14
- edc_listboard/view_mixins/search_listboard_view_mixin.py +6 -7
- edc_metadata/view_mixins/metadata_view_mixin.py +7 -4
- edc_randomization/randomization_list_importer.py +25 -21
- edc_randomization/randomization_list_verifier.py +31 -26
- edc_randomization/randomizer.py +1 -1
- edc_randomization/site_randomizers.py +25 -8
- edc_review_dashboard/middleware.py +4 -4
- edc_review_dashboard/views/subject_review_listboard_view.py +3 -3
- edc_screening/age_evaluator.py +1 -1
- edc_screening/eligibility.py +5 -5
- edc_screening/exceptions.py +3 -3
- edc_screening/gender_evaluator.py +1 -1
- edc_screening/modelform_mixins.py +1 -1
- edc_screening/screening_eligibility.py +30 -35
- edc_screening/utils.py +10 -12
- edc_sites/admin/list_filters.py +2 -2
- edc_sites/admin/site_model_admin_mixin.py +8 -8
- edc_sites/exceptions.py +1 -1
- edc_sites/forms.py +3 -1
- edc_sites/management/commands/sync_sites.py +11 -17
- edc_sites/models/site_profile.py +6 -4
- edc_sites/post_migrate_signals.py +2 -2
- edc_sites/site.py +8 -8
- edc_sites/utils/add_or_update_django_sites.py +3 -3
- edc_sites/utils/valid_site_for_subject_or_raise.py +7 -4
- edc_sites/view_mixins.py +2 -2
- edc_subject_dashboard/view_utils/subject_screening_button.py +5 -3
- edc_view_utils/dashboard_model_button.py +3 -3
- edc_visit_schedule/site_visit_schedules.py +2 -1
- {clinicedc-2.0.23.dist-info → clinicedc-2.0.25.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.23.dist-info → clinicedc-2.0.25.dist-info}/licenses/LICENSE +0 -0
edc_screening/utils.py
CHANGED
|
@@ -40,10 +40,8 @@ def format_reasons_ineligible(*str_values: str | None, delimiter: str | None = N
|
|
|
40
40
|
str_values = str_values or []
|
|
41
41
|
str_values = tuple(x for x in str_values if x)
|
|
42
42
|
if str_values:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
mark_safe(delimiter.join(str_values)), # noqa: S308
|
|
46
|
-
)
|
|
43
|
+
formatted_string = delimiter.join(str_values)
|
|
44
|
+
reasons = mark_safe(formatted_string) # noqa: S308
|
|
47
45
|
return reasons
|
|
48
46
|
|
|
49
47
|
|
|
@@ -120,15 +118,15 @@ def is_eligible_or_raise(
|
|
|
120
118
|
if url and url_name.endswith("changelist"):
|
|
121
119
|
url = f"{url}?q={subject_screening.screening_identifier}"
|
|
122
120
|
if not url:
|
|
123
|
-
|
|
124
|
-
"{}",
|
|
121
|
+
safe_string = mark_safe( # noqa: S308
|
|
125
122
|
"Not allowed. Subject is not eligible. "
|
|
126
|
-
f"Got {subject_screening.screening_identifier}",
|
|
123
|
+
f"Got {subject_screening.screening_identifier}.",
|
|
127
124
|
)
|
|
128
125
|
else:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
safe_string = format_html(
|
|
127
|
+
"Not allowed. Subject is not eligible. See subject "
|
|
128
|
+
'<A href="{url}">{screening_identifier}</A>',
|
|
129
|
+
url=url,
|
|
130
|
+
screening_identifier=subject_screening.screening_identifier,
|
|
133
131
|
)
|
|
134
|
-
raise forms.ValidationError(
|
|
132
|
+
raise forms.ValidationError(safe_string)
|
edc_sites/admin/list_filters.py
CHANGED
|
@@ -17,10 +17,10 @@ class SiteListFilter(SimpleListFilter):
|
|
|
17
17
|
else:
|
|
18
18
|
site_ids = sites.get_site_ids_for_user(request=request)
|
|
19
19
|
for site in Site.objects.filter(id__in=site_ids).order_by("id"):
|
|
20
|
-
names.append((site.id, f"{site.id} {sites.get(site.id).description}"))
|
|
20
|
+
names.append((site.id, f"{site.id} {sites.get(site.id).description}")) # noqa: PERF401
|
|
21
21
|
return tuple(names)
|
|
22
22
|
|
|
23
|
-
def queryset(self, request, queryset):
|
|
23
|
+
def queryset(self, request, queryset): # noqa: ARG002
|
|
24
24
|
if self.value() and self.value() != "none":
|
|
25
25
|
queryset = queryset.filter(site__id=self.value())
|
|
26
26
|
return queryset
|
|
@@ -46,7 +46,7 @@ class SiteModelAdminMixin:
|
|
|
46
46
|
]
|
|
47
47
|
return sites.get_view_only_site_ids_for_user(request=request)
|
|
48
48
|
|
|
49
|
-
def has_viewallsites_permission(self, request, obj=None) -> bool:
|
|
49
|
+
def has_viewallsites_permission(self, request, obj=None) -> bool: # noqa: ARG002
|
|
50
50
|
"""Checks if the user has the EDC custom codename
|
|
51
51
|
"viewallsites" for this model.
|
|
52
52
|
|
|
@@ -54,7 +54,7 @@ class SiteModelAdminMixin:
|
|
|
54
54
|
"""
|
|
55
55
|
opts = self.opts
|
|
56
56
|
codename_allsites = get_permission_codename("viewallsites", opts)
|
|
57
|
-
return request.user.has_perm("
|
|
57
|
+
return request.user.has_perm(f"{opts.app_label}.{codename_allsites}")
|
|
58
58
|
|
|
59
59
|
@admin.display(description="Site", ordering="site__id")
|
|
60
60
|
def site_code(self, obj=None):
|
|
@@ -75,7 +75,7 @@ class SiteModelAdminMixin:
|
|
|
75
75
|
to mulitple sites.
|
|
76
76
|
"""
|
|
77
77
|
list_filter = super().get_list_filter(request)
|
|
78
|
-
list_filter = [x for x in list_filter if x
|
|
78
|
+
list_filter = [x for x in list_filter if x not in ("site", SiteListFilter)]
|
|
79
79
|
if self.user_may_view_other_sites(request) or self.has_viewallsites_permission(
|
|
80
80
|
request
|
|
81
81
|
):
|
|
@@ -95,25 +95,25 @@ class SiteModelAdminMixin:
|
|
|
95
95
|
or self.has_viewallsites_permission(request)
|
|
96
96
|
) and "site" not in list_display:
|
|
97
97
|
list_display = tuple(list_display)
|
|
98
|
-
list_display = list_display[:pos]
|
|
98
|
+
list_display = list_display[:pos], self.site_code, list_display[pos:]
|
|
99
99
|
elif "site" in list_display:
|
|
100
100
|
list_display = tuple(
|
|
101
101
|
[x for x in list_display if x not in ["site", self.site_code]]
|
|
102
102
|
)
|
|
103
|
-
list_display = list_display[:pos]
|
|
103
|
+
list_display = list_display[:pos], self.site_code, list_display[pos:]
|
|
104
104
|
return list_display
|
|
105
105
|
|
|
106
106
|
def get_queryset(self, request) -> QuerySet:
|
|
107
107
|
"""Limit modeladmin queryset for the current site only"""
|
|
108
108
|
qs = super().get_queryset(request)
|
|
109
|
-
site_ids =
|
|
109
|
+
site_ids = (request.site.id, *self.get_view_only_site_ids_for_user(request=request))
|
|
110
110
|
try:
|
|
111
111
|
qs = qs.select_related("site").filter(site_id__in=site_ids)
|
|
112
|
-
except FieldError:
|
|
112
|
+
except FieldError as e:
|
|
113
113
|
raise SiteModeAdminMixinError(
|
|
114
114
|
f"Model missing field `site`. Model `{self.model}`. Did you mean to use "
|
|
115
115
|
f"the SiteModelAdminMixin? See `{self}`."
|
|
116
|
-
)
|
|
116
|
+
) from e
|
|
117
117
|
return qs
|
|
118
118
|
|
|
119
119
|
def get_form(self, request, obj=None, change=False, **kwargs):
|
edc_sites/exceptions.py
CHANGED
edc_sites/forms.py
CHANGED
|
@@ -4,6 +4,8 @@ import sys
|
|
|
4
4
|
from django.conf import settings
|
|
5
5
|
from django.core.management.base import BaseCommand
|
|
6
6
|
from django.core.management.color import color_style
|
|
7
|
+
from multisite.models import Alias
|
|
8
|
+
from multisite.utils import create_or_sync_canonical_from_all_sites
|
|
7
9
|
|
|
8
10
|
from edc_sites.site import sites as site_sites
|
|
9
11
|
from edc_sites.utils import add_or_update_django_sites
|
|
@@ -15,7 +17,6 @@ class Command(BaseCommand):
|
|
|
15
17
|
help = "Add / Update django Site model after changes to edc_sites"
|
|
16
18
|
|
|
17
19
|
def add_arguments(self, parser):
|
|
18
|
-
|
|
19
20
|
parser.add_argument(
|
|
20
21
|
"--ping-hosts",
|
|
21
22
|
default=False,
|
|
@@ -32,26 +33,19 @@ class Command(BaseCommand):
|
|
|
32
33
|
help="Suggest ALLOWED_HOSTS",
|
|
33
34
|
)
|
|
34
35
|
|
|
35
|
-
def handle(self, *args, **options) -> None:
|
|
36
|
+
def handle(self, *args, **options) -> None: # noqa: ARG002
|
|
36
37
|
sys.stdout.write("\n\n")
|
|
37
38
|
sys.stdout.write(" Edc Sites : Adding / Updating sites ... \n")
|
|
38
39
|
add_or_update_django_sites(verbose=True)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
or "multisite.apps.AppConfig" in settings.INSTALLED_APPS
|
|
42
|
-
):
|
|
43
|
-
from multisite.models import Alias
|
|
44
|
-
from multisite.utils import create_or_sync_canonical_from_all_sites
|
|
45
|
-
|
|
46
|
-
sys.stdout.write("\n Multisite. \n")
|
|
47
|
-
create_or_sync_canonical_from_all_sites(verbose=True)
|
|
40
|
+
sys.stdout.write("\n Multisite. \n")
|
|
41
|
+
create_or_sync_canonical_from_all_sites(verbose=True)
|
|
48
42
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
sys.stdout.write(" multisite.Alias \n")
|
|
44
|
+
for obj in Alias.objects.all():
|
|
45
|
+
sys.stdout.write(
|
|
46
|
+
f" - Site model: {obj.site.id}: {obj.domain} "
|
|
47
|
+
f"is_canonical={obj.is_canonical}.\n"
|
|
48
|
+
)
|
|
55
49
|
arg = [arg for arg in sys.argv if arg.startswith("--settings")]
|
|
56
50
|
sys.stdout.write(f"\n Settings: reading from {arg}")
|
|
57
51
|
sys.stdout.write("\n\n Current value of settings.ALLOWED_HOSTS\n")
|
edc_sites/models/site_profile.py
CHANGED
|
@@ -5,17 +5,19 @@ import json
|
|
|
5
5
|
from django.contrib.sites.models import Site
|
|
6
6
|
from django.db import models
|
|
7
7
|
|
|
8
|
+
from edc_constants.constants import NULL_STRING
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
class SiteProfile(models.Model):
|
|
10
12
|
id = models.BigAutoField(primary_key=True)
|
|
11
13
|
|
|
12
|
-
country = models.CharField(max_length=250, default=
|
|
14
|
+
country = models.CharField(max_length=250, default=NULL_STRING)
|
|
13
15
|
|
|
14
|
-
country_code = models.CharField(max_length=15, default=
|
|
16
|
+
country_code = models.CharField(max_length=15, default=NULL_STRING)
|
|
15
17
|
|
|
16
|
-
languages = models.TextField(default=
|
|
18
|
+
languages = models.TextField(default=NULL_STRING)
|
|
17
19
|
|
|
18
|
-
title = models.CharField(max_length=250, default=
|
|
20
|
+
title = models.CharField(max_length=250, default=NULL_STRING)
|
|
19
21
|
|
|
20
22
|
site = models.OneToOneField(Site, on_delete=models.PROTECT)
|
|
21
23
|
|
|
@@ -6,8 +6,8 @@ style = color_style()
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def post_migrate_update_sites(sender=None, **kwargs):
|
|
9
|
-
from .site import sites as site_sites
|
|
10
|
-
from .utils import add_or_update_django_sites
|
|
9
|
+
from .site import sites as site_sites # noqa: PLC0415
|
|
10
|
+
from .utils import add_or_update_django_sites # noqa: PLC0415
|
|
11
11
|
|
|
12
12
|
sys.stdout.write(style.MIGRATE_HEADING("Updating sites:\n"))
|
|
13
13
|
|
edc_sites/site.py
CHANGED
|
@@ -52,23 +52,23 @@ __all__ = [
|
|
|
52
52
|
]
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
class SiteDoesNotExist(Exception):
|
|
55
|
+
class SiteDoesNotExist(Exception): # noqa: N818
|
|
56
56
|
pass
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
class AlreadyRegistered(Exception):
|
|
59
|
+
class AlreadyRegistered(Exception): # noqa: N818
|
|
60
60
|
pass
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
class AlreadyRegisteredName(Exception):
|
|
63
|
+
class AlreadyRegisteredName(Exception): # noqa: N818
|
|
64
64
|
pass
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
class AlreadyRegisteredDomain(Exception):
|
|
67
|
+
class AlreadyRegisteredDomain(Exception): # noqa: N818
|
|
68
68
|
pass
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
class SiteNotRegistered(Exception):
|
|
71
|
+
class SiteNotRegistered(Exception): # noqa: N818
|
|
72
72
|
pass
|
|
73
73
|
|
|
74
74
|
|
|
@@ -157,7 +157,7 @@ class Sites:
|
|
|
157
157
|
for single_site in single_sites:
|
|
158
158
|
if get_insert_uat_subdomain():
|
|
159
159
|
domain = insert_into_domain(single_site.domain, self.uat_subdomain)
|
|
160
|
-
single_site = dataclasses.replace(single_site, domain=domain)
|
|
160
|
+
single_site = dataclasses.replace(single_site, domain=domain) # noqa: PLW2901
|
|
161
161
|
|
|
162
162
|
if single_site.site_id in self._registry:
|
|
163
163
|
raise AlreadyRegistered(f"Site already registered. Got `{single_site}`.")
|
|
@@ -282,8 +282,8 @@ class Sites:
|
|
|
282
282
|
def user_may_view_other_sites(
|
|
283
283
|
self,
|
|
284
284
|
request: WSGIRequest = None,
|
|
285
|
-
user: User = None,
|
|
286
|
-
site_id: int = None,
|
|
285
|
+
user: User | None = None,
|
|
286
|
+
site_id: int | None = None,
|
|
287
287
|
) -> list[int]:
|
|
288
288
|
return self.get_view_only_site_ids_for_user(
|
|
289
289
|
request=request,
|
|
@@ -15,14 +15,14 @@ class UpdateDjangoSitesError(Exception):
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def get_sites():
|
|
18
|
-
from ..site import sites # prevent circular import
|
|
18
|
+
from ..site import sites # prevent circular import # noqa: PLC0415
|
|
19
19
|
|
|
20
20
|
return sites
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def add_or_update_django_sites(
|
|
24
|
-
apps
|
|
25
|
-
single_sites: list[SingleSite] | tuple[SingleSite] = None,
|
|
24
|
+
apps=None,
|
|
25
|
+
single_sites: list[SingleSite] | tuple[SingleSite] | None = None,
|
|
26
26
|
verbose: bool | None = None,
|
|
27
27
|
):
|
|
28
28
|
"""Removes default site and adds/updates given `sites`, etc.
|
|
@@ -30,7 +30,10 @@ def valid_site_for_subject_or_raise(
|
|
|
30
30
|
subject_identifier, raise_exception=True
|
|
31
31
|
)
|
|
32
32
|
if skip_get_current_site:
|
|
33
|
-
warn(
|
|
33
|
+
warn(
|
|
34
|
+
"Skipping validation of current site against registered subject site.",
|
|
35
|
+
stacklevel=2,
|
|
36
|
+
)
|
|
34
37
|
site_obj = registered_subject.site
|
|
35
38
|
else:
|
|
36
39
|
site_obj: Site = get_site_model_cls().objects.get_current()
|
|
@@ -40,15 +43,15 @@ def valid_site_for_subject_or_raise(
|
|
|
40
43
|
site=site_obj,
|
|
41
44
|
raise_exception=True,
|
|
42
45
|
)
|
|
43
|
-
except RegisteredSubjectDoesNotExist:
|
|
46
|
+
except RegisteredSubjectDoesNotExist as e:
|
|
44
47
|
if not registered_subject.site_id:
|
|
45
48
|
raise InvalidSiteForSubjectError(
|
|
46
49
|
"Site not defined for registered subject! "
|
|
47
50
|
f"Subject identifier=`{subject_identifier}`. "
|
|
48
|
-
)
|
|
51
|
+
) from e
|
|
49
52
|
raise InvalidSiteForSubjectError(
|
|
50
53
|
f"Invalid site for subject. Subject identifier=`{subject_identifier}`. "
|
|
51
54
|
f"Expected `{registered_subject.site.name}`. "
|
|
52
55
|
f"Got site_id=`{site_obj.id}`"
|
|
53
|
-
)
|
|
56
|
+
) from e
|
|
54
57
|
return site_obj
|
edc_sites/view_mixins.py
CHANGED
|
@@ -19,10 +19,10 @@ class SiteViewMixin:
|
|
|
19
19
|
kwargs.update(site_profile=site_profile)
|
|
20
20
|
try:
|
|
21
21
|
kwargs.update(site_title=site_profile.title)
|
|
22
|
-
except AttributeError:
|
|
22
|
+
except AttributeError as e:
|
|
23
23
|
if not sites.all():
|
|
24
24
|
raise SiteNotRegistered(
|
|
25
25
|
"Unable to determine site profile 'title'. No sites have been registered! "
|
|
26
|
-
)
|
|
26
|
+
) from e
|
|
27
27
|
raise
|
|
28
28
|
return kwargs
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
from django.contrib.sites.models import Site
|
|
@@ -11,16 +11,18 @@ from django.utils.translation import gettext as _
|
|
|
11
11
|
from edc_view_utils import DashboardModelButton
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
|
+
from edc_model.models import BaseUuidModel
|
|
14
15
|
from edc_screening.model_mixins import ScreeningModelMixin
|
|
15
16
|
|
|
16
|
-
ScreeningModel
|
|
17
|
+
class ScreeningModel(ScreeningModelMixin, BaseUuidModel): ...
|
|
18
|
+
|
|
17
19
|
|
|
18
20
|
__all__ = ["SubjectScreeningButton"]
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
@dataclass
|
|
22
24
|
class SubjectScreeningButton(DashboardModelButton):
|
|
23
|
-
model_obj: ScreeningModel = None
|
|
25
|
+
model_obj: ScreeningModel | None = None
|
|
24
26
|
metadata_model_obj: models.Model = field(init=False)
|
|
25
27
|
|
|
26
28
|
def __post_init__(self):
|
|
@@ -34,9 +34,9 @@ class DashboardModelButton(ModelButton):
|
|
|
34
34
|
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
model_obj: CrfModel | RequisitionModel =
|
|
38
|
-
metadata_model_obj: CrfMetadata | RequisitionMetadata = None
|
|
39
|
-
appointment: Appointment = None
|
|
37
|
+
model_obj: CrfModel | RequisitionModel | None = None
|
|
38
|
+
metadata_model_obj: CrfMetadata | RequisitionMetadata | None = None
|
|
39
|
+
appointment: Appointment | None = None
|
|
40
40
|
next_url_name: str = field(default="subject_dashboard_url")
|
|
41
41
|
model_cls: type[CrfModel | RequisitionModel] = field(default=None, init=False)
|
|
42
42
|
|
|
@@ -93,7 +93,8 @@ class SiteVisitSchedules:
|
|
|
93
93
|
return visit_schedules or self.registry
|
|
94
94
|
|
|
95
95
|
def get_by_consent_definition(
|
|
96
|
-
self,
|
|
96
|
+
self,
|
|
97
|
+
cdef: ConsentDefinition,
|
|
97
98
|
) -> tuple[VisitSchedule, Schedule]:
|
|
98
99
|
"""Returns a visit schedule instance or raises."""
|
|
99
100
|
ret = []
|
|
File without changes
|
|
File without changes
|