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
edc_auth/admin/role_admin.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
|
-
from django.utils.html import format_html
|
|
3
2
|
from django.utils.safestring import mark_safe
|
|
4
3
|
|
|
5
4
|
from edc_model_admin.mixins import TemplatesModelAdminMixin
|
|
@@ -12,22 +11,20 @@ from ..models import Role
|
|
|
12
11
|
class RoleAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
13
12
|
fieldsets = ((None, ({"fields": ("display_name", "name", "display_index", "groups")})),)
|
|
14
13
|
|
|
15
|
-
list_display_links
|
|
14
|
+
list_display_links = ("display_name", "group_list")
|
|
16
15
|
|
|
17
|
-
list_display
|
|
16
|
+
list_display = ("display_name", "name", "group_list")
|
|
18
17
|
|
|
19
|
-
filter_horizontal
|
|
18
|
+
filter_horizontal = ("groups",)
|
|
20
19
|
|
|
21
|
-
search_fields
|
|
20
|
+
search_fields = ("display_name", "name", "groups__name")
|
|
22
21
|
|
|
23
|
-
ordering
|
|
22
|
+
ordering = ("display_index", "display_name")
|
|
24
23
|
|
|
25
24
|
list_filter = ("groups__name",)
|
|
26
25
|
|
|
27
26
|
@staticmethod
|
|
28
27
|
def group_list(obj=None) -> str:
|
|
29
|
-
group_names = []
|
|
30
|
-
for group in obj.groups.all():
|
|
31
|
-
group_names.append(group.name)
|
|
28
|
+
group_names = [group.name for group in obj.groups.all()]
|
|
32
29
|
group_names.sort()
|
|
33
|
-
return
|
|
30
|
+
return mark_safe("<BR>".join(group_names)) # noqa: S308
|
edc_auth/admin/user_admin.py
CHANGED
|
@@ -31,8 +31,8 @@ class UserAdmin(TemplatesModelAdminMixin, BaseUserAdmin):
|
|
|
31
31
|
show_object_tools: bool = True
|
|
32
32
|
|
|
33
33
|
form = UserChangeForm
|
|
34
|
-
inlines =
|
|
35
|
-
actions =
|
|
34
|
+
inlines = (UserProfileInline,)
|
|
35
|
+
actions = (send_new_credentials_to_user_action,)
|
|
36
36
|
|
|
37
37
|
list_display = (
|
|
38
38
|
"username",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
|
-
from django.utils.html import format_html
|
|
3
2
|
from django.utils.safestring import mark_safe
|
|
4
3
|
|
|
5
4
|
from edc_model_admin.mixins import TemplatesModelAdminMixin
|
|
@@ -45,21 +44,18 @@ class UserProfileAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
45
44
|
@staticmethod
|
|
46
45
|
def user_sites(obj=None):
|
|
47
46
|
site_names = [o.name for o in obj.sites.all().order_by("name")]
|
|
48
|
-
return
|
|
47
|
+
return mark_safe("<BR>".join(site_names)) # noqa: S308
|
|
49
48
|
|
|
50
49
|
@staticmethod
|
|
51
50
|
def user_email_notifications(obj=None):
|
|
52
51
|
display_names = [
|
|
53
52
|
o.display_name for o in obj.email_notifications.all().order_by("display_name")
|
|
54
53
|
]
|
|
55
|
-
return
|
|
54
|
+
return mark_safe("<BR>".join(display_names)) # noqa: S308
|
|
56
55
|
|
|
57
56
|
@staticmethod
|
|
58
57
|
def user_sms_notifications(obj=None):
|
|
59
58
|
display_names = [
|
|
60
59
|
o.display_name for o in obj.sms_notifications.all().order_by("display_name")
|
|
61
60
|
]
|
|
62
|
-
return
|
|
63
|
-
"{}",
|
|
64
|
-
mark_safe("<BR>".join(display_names)), # nosec B703, B308
|
|
65
|
-
)
|
|
61
|
+
return mark_safe("<BR>".join(display_names)) # noqa: S308
|
edc_auth/apps.py
CHANGED
|
@@ -11,9 +11,9 @@ class AppConfig(DjangoAppConfig):
|
|
|
11
11
|
default_auto_field = "django.db.models.BigAutoField"
|
|
12
12
|
|
|
13
13
|
def ready(self):
|
|
14
|
-
from edc_appconfig.system_checks import check_for_edc_appconfig
|
|
14
|
+
from edc_appconfig.system_checks import check_for_edc_appconfig # noqa: PLC0415
|
|
15
15
|
|
|
16
|
-
from .system_checks import (
|
|
16
|
+
from .system_checks import ( # noqa: PLC0415
|
|
17
17
|
check_auth_updater,
|
|
18
18
|
check_etc_dir,
|
|
19
19
|
check_site_auths,
|
|
@@ -139,7 +139,7 @@ class GroupUpdater:
|
|
|
139
139
|
errmsg = f"{e} Got codename={codename},app_label={app_label}"
|
|
140
140
|
if not self.warn_only:
|
|
141
141
|
raise CodenameDoesNotExist(errmsg) from e
|
|
142
|
-
warn(style.ERROR(errmsg))
|
|
142
|
+
warn(style.ERROR(errmsg), stacklevel=2)
|
|
143
143
|
except MultipleObjectsReturned as e:
|
|
144
144
|
if not allow_multiple_objects:
|
|
145
145
|
self.delete_and_raise_on_duplicate_codenames(
|
|
@@ -219,7 +219,7 @@ class GroupUpdater:
|
|
|
219
219
|
try:
|
|
220
220
|
model_cls = self.apps.get_model(model)
|
|
221
221
|
except LookupError as e:
|
|
222
|
-
warn(f"{e}. Got {model}")
|
|
222
|
+
warn(f"{e}. Got {model}", stacklevel=2)
|
|
223
223
|
else:
|
|
224
224
|
content_type = self.content_type_model_cls.objects.get_for_model(model_cls)
|
|
225
225
|
for codename_tpl in codename_tuples:
|
|
@@ -282,8 +282,8 @@ class GroupUpdater:
|
|
|
282
282
|
|
|
283
283
|
def remove_permissions_by_codenames(
|
|
284
284
|
self,
|
|
285
|
-
group
|
|
286
|
-
codenames: list[str]
|
|
285
|
+
group,
|
|
286
|
+
codenames: list[str],
|
|
287
287
|
allow_multiple_objects: bool | None = None,
|
|
288
288
|
):
|
|
289
289
|
"""Remove the given codenames from the given group."""
|
|
@@ -36,8 +36,7 @@ class RoleUpdater:
|
|
|
36
36
|
"""
|
|
37
37
|
if self.verbose:
|
|
38
38
|
sys.stdout.write(style.MIGRATE_HEADING(" - Updating roles:\n"))
|
|
39
|
-
index
|
|
40
|
-
for role_name, groups in self.roles.items():
|
|
39
|
+
for index, (role_name, groups) in enumerate(self.roles.items()):
|
|
41
40
|
display_name = role_name.replace("_", " ").lower().title()
|
|
42
41
|
if self.verbose:
|
|
43
42
|
sys.stdout.write(f" * updating groups for {role_name}.\n")
|
|
@@ -63,7 +62,6 @@ class RoleUpdater:
|
|
|
63
62
|
) from e
|
|
64
63
|
# group = self.group_model_cls.objects.create(name=group_name)
|
|
65
64
|
role.groups.add(group)
|
|
66
|
-
index += 1
|
|
67
65
|
if self.verbose:
|
|
68
66
|
sys.stdout.write(" Done.\n")
|
|
69
67
|
sys.stdout.flush()
|
edc_auth/backends.py
CHANGED
|
@@ -10,7 +10,7 @@ class ModelBackendWithSite(ModelBackend):
|
|
|
10
10
|
associated with the current SITE_ID.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
def authenticate(self, request, username=None, password=None, **kwargs):
|
|
13
|
+
def authenticate(self, request, username=None, password=None, **kwargs): # noqa: ARG002
|
|
14
14
|
user = super().authenticate(request, username=username, password=password)
|
|
15
15
|
if user:
|
|
16
16
|
sites = [obj.id for obj in user.userprofile.sites.all()]
|
edc_auth/export_users.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import csv
|
|
2
|
-
from
|
|
2
|
+
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
from django.contrib.auth.models import User
|
|
5
|
+
from django.utils import timezone
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def export_users(path):
|
|
8
|
-
path = path or f"edc_users_{
|
|
9
|
+
path = path or f"edc_users_{timezone.now().strftime('%Y%m%d%H%M%S')}.csv"
|
|
9
10
|
user = {
|
|
10
11
|
"username": None,
|
|
11
12
|
"password": None,
|
|
@@ -21,13 +22,13 @@ def export_users(path):
|
|
|
21
22
|
"role_names": None,
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
with open(
|
|
25
|
+
with Path(path).open("w+") as f:
|
|
25
26
|
writer = csv.DictWriter(f, fieldnames=user, delimiter="|")
|
|
26
27
|
writer.writeheader()
|
|
27
28
|
for user in User.objects.all().order_by("username"):
|
|
28
29
|
site_names = ",".join([s.name for s in user.userprofile.sites.all()])
|
|
29
30
|
role_names = ",".join([g.name for g in user.userprofile.roles.all()])
|
|
30
|
-
user = {
|
|
31
|
+
user = { # noqa: PLW2901
|
|
31
32
|
"username": user.username,
|
|
32
33
|
"password": user.password,
|
|
33
34
|
"is_staff": user.is_staff,
|
|
@@ -42,4 +43,4 @@ def export_users(path):
|
|
|
42
43
|
"role_names": role_names or "",
|
|
43
44
|
}
|
|
44
45
|
writer.writerow(user)
|
|
45
|
-
print(f"Done. See file `{path}` in the current directory.")
|
|
46
|
+
print(f"Done. See file `{path}` in the current directory.") # noqa: T201
|
|
@@ -33,23 +33,23 @@ class ExportPermissionsFixer:
|
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
35
|
if self.verbose:
|
|
36
|
-
print("Adding `import` and `export` to default permissions.")
|
|
36
|
+
print("Adding `import` and `export` to default permissions.") # noqa: T201
|
|
37
37
|
for app_config in self.app_configs:
|
|
38
38
|
if self.verbose:
|
|
39
|
-
print(f" * updating {app_config.name}")
|
|
39
|
+
print(f" * updating {app_config.name}") # noqa: T201
|
|
40
40
|
for model in app_config.get_models():
|
|
41
41
|
self.fix_for_model(model)
|
|
42
42
|
if self.verbose:
|
|
43
|
-
print("Done")
|
|
43
|
+
print("Done") # noqa: T201
|
|
44
44
|
|
|
45
45
|
def fix_for_model(self, model):
|
|
46
|
-
from edc_model import models as edc_models
|
|
46
|
+
from edc_model import models as edc_models # noqa: PLC0415
|
|
47
47
|
|
|
48
48
|
if issubclass(model, (edc_models.BaseUuidModel,)):
|
|
49
49
|
permission_model_cls = django_apps.get_model("auth.permission")
|
|
50
50
|
content_type_model_cls = django_apps.get_model("contenttypes.contenttype")
|
|
51
51
|
if self.verbose:
|
|
52
|
-
print(f" - {model._meta.label_lower}")
|
|
52
|
+
print(f" - {model._meta.label_lower}") # noqa: T201
|
|
53
53
|
try:
|
|
54
54
|
app_label, model_name = model._meta.label_lower.split(".")
|
|
55
55
|
content_type = content_type_model_cls.objects.get(
|
|
@@ -58,9 +58,9 @@ class ExportPermissionsFixer:
|
|
|
58
58
|
)
|
|
59
59
|
except ObjectDoesNotExist as e:
|
|
60
60
|
if self.warn_only:
|
|
61
|
-
warn(f"ObjectDoesNotExist: {e} Got {model}.")
|
|
61
|
+
warn(f"ObjectDoesNotExist: {e} Got {model}.", stacklevel=2)
|
|
62
62
|
else:
|
|
63
|
-
raise ObjectDoesNotExist(f"{e} Got {model}.")
|
|
63
|
+
raise ObjectDoesNotExist(f"{e} Got {model}.") from e
|
|
64
64
|
else:
|
|
65
65
|
for action in ["import", "export"]:
|
|
66
66
|
codename = f"{action}_{model._meta.label_lower.split('.')[1]}"
|
|
@@ -71,7 +71,7 @@ class ExportPermissionsFixer:
|
|
|
71
71
|
opts.update(name=f"Can {action} {model._meta.verbose_name}")
|
|
72
72
|
permission_model_cls.objects.create(**opts)
|
|
73
73
|
if self.verbose:
|
|
74
|
-
print(f" created for {model._meta.label_lower}")
|
|
74
|
+
print(f" created for {model._meta.label_lower}") # noqa: T201
|
|
75
75
|
else:
|
|
76
76
|
obj.name = f"Can {action} {model._meta.verbose_name}"
|
|
77
77
|
obj.save()
|
edc_auth/forms.py
CHANGED
|
@@ -45,36 +45,33 @@ class UserProfileForm(forms.ModelForm):
|
|
|
45
45
|
raise forms.ValidationError(
|
|
46
46
|
{
|
|
47
47
|
"is_multisite_viewer": (
|
|
48
|
-
"Invalid. User has change permissions to other objects. "
|
|
49
|
-
f"Got {c}"
|
|
48
|
+
f"Invalid. User has change permissions to other objects. Got {c}"
|
|
50
49
|
)
|
|
51
50
|
}
|
|
52
51
|
)
|
|
53
52
|
|
|
54
53
|
qs = self.cleaned_data.get("email_notifications")
|
|
55
|
-
if qs and qs.count() > 0:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
54
|
+
if qs and qs.count() > 0 and not get_email_enabled():
|
|
55
|
+
raise forms.ValidationError(
|
|
56
|
+
{
|
|
57
|
+
"email_notifications": (
|
|
58
|
+
"You may not choose an email "
|
|
59
|
+
"notification. Email is not enabled. "
|
|
60
|
+
"Contact your EDC administrator."
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
)
|
|
66
64
|
qs = self.cleaned_data.get("sms_notifications")
|
|
67
|
-
if qs and qs.count() > 0:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
65
|
+
if qs and qs.count() > 0 and not settings.TWILIO_ENABLED:
|
|
66
|
+
raise forms.ValidationError(
|
|
67
|
+
{
|
|
68
|
+
"sms_notifications": (
|
|
69
|
+
"You may not choose an SMS "
|
|
70
|
+
"notification. SMS is not enabled. "
|
|
71
|
+
"Contact your EDC administrator."
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
)
|
|
78
75
|
qs = self.cleaned_data.get("roles")
|
|
79
76
|
if qs and qs.count() > 0:
|
|
80
77
|
for role in qs:
|
edc_auth/get_app_codenames.py
CHANGED
|
@@ -38,7 +38,7 @@ def get_app_codenames(
|
|
|
38
38
|
pass
|
|
39
39
|
else:
|
|
40
40
|
for model_cls in get_models(app_config, exclude_models):
|
|
41
|
-
codenames.append(f"{app_config.name}.view_{model_cls._meta.model_name}")
|
|
41
|
+
codenames.append(f"{app_config.name}.view_{model_cls._meta.model_name}") # noqa: PERF401
|
|
42
42
|
for name in crud_apps:
|
|
43
43
|
try:
|
|
44
44
|
app_config = django_apps.get_app_config(name)
|
|
@@ -80,5 +80,5 @@ def get_codename(
|
|
|
80
80
|
]
|
|
81
81
|
prefixes = [f"{s}_" for s in permissions]
|
|
82
82
|
for prefix in prefixes:
|
|
83
|
-
codenames.append(f"{app_name}.{prefix}{model_name}")
|
|
83
|
+
codenames.append(f"{app_name}.{prefix}{model_name}") # noqa: PERF401
|
|
84
84
|
return codenames
|
edc_auth/import_users.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import csv
|
|
2
|
-
import os
|
|
3
2
|
import re
|
|
4
|
-
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
from string import Template
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
@@ -9,6 +9,7 @@ from django.contrib.auth.models import User
|
|
|
9
9
|
from django.contrib.sites.models import Site
|
|
10
10
|
from django.core.exceptions import ObjectDoesNotExist
|
|
11
11
|
from django.core.mail import EmailMessage
|
|
12
|
+
from django.utils import timezone
|
|
12
13
|
from mempass import PasswordGenerator
|
|
13
14
|
|
|
14
15
|
from edc_protocol.research_protocol_config import ResearchProtocolConfig
|
|
@@ -89,8 +90,8 @@ def import_users(
|
|
|
89
90
|
site_names: a comma-separated list of sites
|
|
90
91
|
role_names: a comma-separated list of roles
|
|
91
92
|
"""
|
|
92
|
-
path =
|
|
93
|
-
with open(
|
|
93
|
+
path = Path(path)
|
|
94
|
+
with path.open() as f:
|
|
94
95
|
reader = csv.DictReader(f, delimiter="|")
|
|
95
96
|
for csv_data in reader:
|
|
96
97
|
opts: dict = {}
|
|
@@ -111,8 +112,8 @@ def import_users(
|
|
|
111
112
|
opts.update(email=csv_data.get("email"))
|
|
112
113
|
opts.update(alternate_email=csv_data.get("alternate_email"))
|
|
113
114
|
opts.update(job_title=csv_data.get("job_title"))
|
|
114
|
-
opts.update(is_active=
|
|
115
|
-
opts.update(is_staff=
|
|
115
|
+
opts.update(is_active=csv_data.get("is_active") == "True")
|
|
116
|
+
opts.update(is_staff=csv_data.get("is_staff") == "True")
|
|
116
117
|
UserImporter(
|
|
117
118
|
resource_name=resource_name,
|
|
118
119
|
send_email_to_user=send_email_to_user,
|
|
@@ -122,7 +123,7 @@ def import_users(
|
|
|
122
123
|
**kwargs,
|
|
123
124
|
)
|
|
124
125
|
if export_to_file:
|
|
125
|
-
export_users(f"edc_users_imported_{
|
|
126
|
+
export_users(f"edc_users_imported_{timezone.now().strftime('%Y%m%d%H%M%S')}")
|
|
126
127
|
|
|
127
128
|
|
|
128
129
|
class UserImporter:
|
|
@@ -133,18 +134,18 @@ class UserImporter:
|
|
|
133
134
|
|
|
134
135
|
def __init__(
|
|
135
136
|
self,
|
|
136
|
-
username: str = None,
|
|
137
|
-
first_name: str = None,
|
|
138
|
-
last_name: str = None,
|
|
139
|
-
job_title: str = None,
|
|
140
|
-
email: str = None,
|
|
141
|
-
alternate_email: str = None,
|
|
142
|
-
mobile: str = None,
|
|
143
|
-
site_names: list[str] = None,
|
|
144
|
-
role_names: list[str] = None,
|
|
145
|
-
is_active: bool = None,
|
|
146
|
-
is_staff: bool = None,
|
|
147
|
-
resource_name: str = None,
|
|
137
|
+
username: str | None = None,
|
|
138
|
+
first_name: str | None = None,
|
|
139
|
+
last_name: str | None = None,
|
|
140
|
+
job_title: str | None = None,
|
|
141
|
+
email: str | None = None,
|
|
142
|
+
alternate_email: str | None = None,
|
|
143
|
+
mobile: str | None = None,
|
|
144
|
+
site_names: list[str] | None = None,
|
|
145
|
+
role_names: list[str] | None = None,
|
|
146
|
+
is_active: bool | None = None,
|
|
147
|
+
is_staff: bool | None = None,
|
|
148
|
+
resource_name: str | None = None,
|
|
148
149
|
created_email_template: Template | None = None,
|
|
149
150
|
updated_email_template: Template | None = None,
|
|
150
151
|
send_email_to_user: Any | None = None,
|
|
@@ -200,7 +201,7 @@ class UserImporter:
|
|
|
200
201
|
try:
|
|
201
202
|
self.email_message.send(fail_silently=False)
|
|
202
203
|
except ConnectionRefusedError:
|
|
203
|
-
|
|
204
|
+
sys.stdout.write(f"{self.email_message.body}\n")
|
|
204
205
|
|
|
205
206
|
def validate_username(self) -> None:
|
|
206
207
|
if not self.username:
|
|
@@ -241,12 +242,12 @@ class UserImporter:
|
|
|
241
242
|
for site_name in site_names:
|
|
242
243
|
try:
|
|
243
244
|
site = Site.objects.get(name=site_name)
|
|
244
|
-
except ObjectDoesNotExist:
|
|
245
|
+
except ObjectDoesNotExist as e:
|
|
245
246
|
sites = [s.name for s in Site.objects.all()]
|
|
246
247
|
raise UserImporterError(
|
|
247
248
|
f"Unknown site for user. Expected one of {sites}. "
|
|
248
249
|
f"Got {self.user.username}, {site_name}."
|
|
249
|
-
)
|
|
250
|
+
) from e
|
|
250
251
|
else:
|
|
251
252
|
self.user.userprofile.sites.add(site)
|
|
252
253
|
|
|
@@ -256,11 +257,11 @@ class UserImporter:
|
|
|
256
257
|
for role_name in role_names:
|
|
257
258
|
try:
|
|
258
259
|
role = Role.objects.get(name__iexact=role_name)
|
|
259
|
-
except ObjectDoesNotExist:
|
|
260
|
+
except ObjectDoesNotExist as e:
|
|
260
261
|
raise UserImporterError(
|
|
261
262
|
f"Unknown role for user. Got role `{role_name}` "
|
|
262
263
|
f"for user `{self.user.username}`"
|
|
263
|
-
)
|
|
264
|
+
) from e
|
|
264
265
|
else:
|
|
265
266
|
self.user.userprofile.roles.add(role)
|
|
266
267
|
|
|
@@ -25,11 +25,11 @@ class Command(BaseCommand):
|
|
|
25
25
|
"--email", action="store_true", dest="email", help="Alternate email"
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
def handle(self, *args, **options):
|
|
28
|
+
def handle(self, *args, **options): # noqa: ARG002
|
|
29
29
|
try:
|
|
30
30
|
user = User.objects.get(username=options["username"])
|
|
31
31
|
except ObjectDoesNotExist as e:
|
|
32
|
-
raise CommandError(e)
|
|
32
|
+
raise CommandError(e) from e
|
|
33
33
|
else:
|
|
34
34
|
p = PasswordSetter(super_username=options["super_user"])
|
|
35
35
|
p.reset_user(username=options["username"])
|
edc_auth/models/signals.py
CHANGED
|
@@ -10,7 +10,7 @@ from .user_profile import UserProfile
|
|
|
10
10
|
def update_user_profile_on_post_save(sender, instance, raw, **kwargs):
|
|
11
11
|
if not raw:
|
|
12
12
|
try:
|
|
13
|
-
instance.userprofile
|
|
13
|
+
instance.userprofile # noqa: B018
|
|
14
14
|
except ObjectDoesNotExist:
|
|
15
15
|
UserProfile.objects.create(user=instance)
|
|
16
16
|
|
edc_auth/models/user_profile.py
CHANGED
|
@@ -4,7 +4,6 @@ from django.core.validators import RegexValidator
|
|
|
4
4
|
from django.db import models
|
|
5
5
|
from django.db.models.deletion import CASCADE
|
|
6
6
|
from django.utils.html import format_html
|
|
7
|
-
from django.utils.safestring import mark_safe
|
|
8
7
|
from django.utils.translation import gettext_lazy as _
|
|
9
8
|
|
|
10
9
|
from edc_export.choices import EXPORT_FORMATS
|
|
@@ -50,7 +49,7 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
|
|
|
50
49
|
blank=True,
|
|
51
50
|
help_text=format_html(
|
|
52
51
|
'Change in <a href="{href}">{label}</a>',
|
|
53
|
-
href=
|
|
52
|
+
href="/edc_label/",
|
|
54
53
|
label="Edc Label Administration",
|
|
55
54
|
),
|
|
56
55
|
)
|
|
@@ -61,7 +60,7 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
|
|
|
61
60
|
blank=True,
|
|
62
61
|
help_text=format_html(
|
|
63
62
|
'Change in <a href="{href}">{label}</a>',
|
|
64
|
-
href=
|
|
63
|
+
href="/edc_label/",
|
|
65
64
|
label="Edc Label Administration",
|
|
66
65
|
),
|
|
67
66
|
)
|
|
@@ -72,7 +71,7 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
|
|
|
72
71
|
blank=True,
|
|
73
72
|
help_text=format_html(
|
|
74
73
|
'Change in <a href="{href}">{label}</a>',
|
|
75
|
-
href=
|
|
74
|
+
href="/edc_label/",
|
|
76
75
|
label="Edc Label Administration",
|
|
77
76
|
),
|
|
78
77
|
)
|
|
@@ -91,7 +90,7 @@ class UserProfile(NotificationUserProfileModelMixin, models.Model):
|
|
|
91
90
|
def __str__(self):
|
|
92
91
|
return self.user.username
|
|
93
92
|
|
|
94
|
-
def add_groups_for_roles(self, pk_set):
|
|
93
|
+
def add_groups_for_roles(self, pk_set): # noqa: ARG002
|
|
95
94
|
"""Add groups to this user for the selected roles.
|
|
96
95
|
|
|
97
96
|
Called by m2m signal.
|
edc_auth/password_setter.py
CHANGED
|
@@ -37,17 +37,17 @@ class PasswordSetter:
|
|
|
37
37
|
def get_administrator_fullname(super_username: str) -> str:
|
|
38
38
|
try:
|
|
39
39
|
obj = User.objects.get(username=super_username)
|
|
40
|
-
except ObjectDoesNotExist:
|
|
40
|
+
except ObjectDoesNotExist as e:
|
|
41
41
|
raise PasswordSetterError(
|
|
42
42
|
"Need the username of the administrator / super user. Got None."
|
|
43
|
-
)
|
|
43
|
+
) from e
|
|
44
44
|
return f"{obj.first_name} {obj.last_name}"
|
|
45
45
|
|
|
46
46
|
def reset_all(self) -> None:
|
|
47
47
|
users = User.objects.filter(is_active=True, is_staff=True, is_superuser=False)
|
|
48
48
|
self._reset(users)
|
|
49
49
|
|
|
50
|
-
def reset_by_groups(self, group_names: list[str]
|
|
50
|
+
def reset_by_groups(self, group_names: list[str]) -> None:
|
|
51
51
|
users = User.objects.filter(
|
|
52
52
|
groups__name__in=group_names,
|
|
53
53
|
is_active=True,
|
|
@@ -64,7 +64,7 @@ class PasswordSetter:
|
|
|
64
64
|
usernames = [username]
|
|
65
65
|
self.reset_users(usernames)
|
|
66
66
|
|
|
67
|
-
def reset_by_sites(self, site_names: list[str]
|
|
67
|
+
def reset_by_sites(self, site_names: list[str]) -> None:
|
|
68
68
|
users = User.objects.filter(
|
|
69
69
|
userprofile__sites__name__in=site_names,
|
|
70
70
|
is_active=True,
|
edc_auth/post_migrate_signals.py
CHANGED
|
@@ -9,8 +9,8 @@ style = color_style()
|
|
|
9
9
|
def post_migrate_user_groups_and_roles(sender=None, **kwargs):
|
|
10
10
|
"""Update Groups, Role model with EDC defaults."""
|
|
11
11
|
|
|
12
|
-
from .auth_updater import AuthUpdater
|
|
13
|
-
from .auth_updater.group_updater import CodenameDoesNotExist
|
|
12
|
+
from .auth_updater import AuthUpdater # noqa: PLC0415
|
|
13
|
+
from .auth_updater.group_updater import CodenameDoesNotExist # noqa: PLC0415
|
|
14
14
|
|
|
15
15
|
try:
|
|
16
16
|
AuthUpdater(apps=django_apps, verbose=True)
|
|
@@ -18,7 +18,7 @@ def change_password(user, nwords: int | None = None) -> str:
|
|
|
18
18
|
return password
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def send_new_credentials_to_user(user, nwords: int | None = None) ->
|
|
21
|
+
def send_new_credentials_to_user(user, nwords: int | None = None) -> int:
|
|
22
22
|
body = change_user_template
|
|
23
23
|
site_names = "\n - ".join([s.name for s in user.userprofile.sites.all()])
|
|
24
24
|
role_names = "\n - ".join([r.display_name for r in user.userprofile.roles.all()])
|
edc_auth/system_checks.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
from django.conf import settings
|
|
4
|
-
from django.core.checks import CheckMessage, Warning
|
|
5
|
+
from django.core.checks import CheckMessage, Warning # noqa: A004
|
|
5
6
|
from django.core.management import color_style
|
|
6
7
|
|
|
7
8
|
style = color_style()
|
|
@@ -10,7 +11,7 @@ style = color_style()
|
|
|
10
11
|
def check_etc_dir(app_configs, **kwargs) -> list[CheckMessage]:
|
|
11
12
|
errors = []
|
|
12
13
|
try:
|
|
13
|
-
settings.ETC_DIR
|
|
14
|
+
settings.ETC_DIR # noqa: B018
|
|
14
15
|
except AttributeError:
|
|
15
16
|
pass
|
|
16
17
|
else:
|
|
@@ -37,11 +38,11 @@ def check_etc_dir(app_configs, **kwargs) -> list[CheckMessage]:
|
|
|
37
38
|
def check_static_root(app_configs, **kwargs) -> list[CheckMessage]:
|
|
38
39
|
errors = []
|
|
39
40
|
try:
|
|
40
|
-
settings.STATIC_ROOT
|
|
41
|
+
settings.STATIC_ROOT # noqa: B018
|
|
41
42
|
except AttributeError:
|
|
42
43
|
pass
|
|
43
44
|
else:
|
|
44
|
-
if settings.STATIC_ROOT and not
|
|
45
|
+
if settings.STATIC_ROOT and not Path(settings.STATIC_ROOT).exists():
|
|
45
46
|
errors.append(
|
|
46
47
|
Warning(
|
|
47
48
|
f"Folder does not exist. Got {settings.STATIC_ROOT}",
|
|
@@ -54,7 +55,7 @@ def check_static_root(app_configs, **kwargs) -> list[CheckMessage]:
|
|
|
54
55
|
def check_auth_updater(app_configs, **kwargs) -> list[CheckMessage]:
|
|
55
56
|
errors = []
|
|
56
57
|
try:
|
|
57
|
-
settings.EDC_AUTH_SKIP_AUTH_UPDATER
|
|
58
|
+
settings.EDC_AUTH_SKIP_AUTH_UPDATER # noqa: B018
|
|
58
59
|
except AttributeError:
|
|
59
60
|
pass
|
|
60
61
|
else:
|
|
@@ -72,7 +73,7 @@ def check_auth_updater(app_configs, **kwargs) -> list[CheckMessage]:
|
|
|
72
73
|
def check_site_auths(app_configs, **kwargs) -> list[CheckMessage]:
|
|
73
74
|
errors = []
|
|
74
75
|
try:
|
|
75
|
-
settings.EDC_AUTH_SKIP_AUTH_UPDATER
|
|
76
|
+
settings.EDC_AUTH_SKIP_AUTH_UPDATER # noqa: B018
|
|
76
77
|
except AttributeError:
|
|
77
78
|
pass
|
|
78
79
|
else:
|