clinicedc 2.0.25__py3-none-any.whl → 2.0.27__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clinicedc
3
- Version: 2.0.25
3
+ Version: 2.0.27
4
4
  Summary: A clinical trials data management framework built on Django
5
5
  Keywords: django,clinicedc,edc,clinical trials,research,data management,esource
6
6
  Author: Erik van Widenfelt, Jonathan Willitts
@@ -611,8 +611,8 @@ edc_consent/views/home_view.py,sha256=TRqEcysATqzVGTHPhn6HxPsWjIGAPDwYaCylIJOWM7
611
611
  edc_consent/wsgi.py,sha256=IuY8oXnc033507PBykHhWuZxUDpg4MaMvTmVSM6uJ54,390
612
612
  edc_constants/__init__.py,sha256=jHg-osFAN-UgMbnvV1MnQ0sD37lq0QisRbJ_oqrGUdw,84
613
613
  edc_constants/apps.py,sha256=H3gCke2ogPgCL2sSD44Nb56ZBurei4uXWEhcUTdIlYI,318
614
- edc_constants/choices.py,sha256=sTap6YDFb3ydlm4dLewt0E4X-q9lHCRHR5aDX28SdV4,19094
615
- edc_constants/constants.py,sha256=ZVHT0VazXzdt4cjt5wKRXu6mRNRY5JoDADDO3a4WOrc,5356
614
+ edc_constants/choices.py,sha256=OvsfqZvb872tANzN0LMLgGLMkqaqRRb1FooXBcTr4cY,19638
615
+ edc_constants/constants.py,sha256=Utlax5PHmnKXe0DQebnz7aEwlE1KM9ItUEET6jPqgN0,5585
616
616
  edc_constants/context_processor.py,sha256=JwfB03QXX9AFcVI_-ohdNVTGWpP2DDpH_t_kdiAe-7s,809
617
617
  edc_constants/date_constants.py,sha256=5XrhZHz0caO1qghW4_r2slDMVdHsz6QG8OfSo2mMKSg,616
618
618
  edc_constants/disease_constants.py,sha256=ZXzwomCaDMtrghHkAHdUUuNbZloIFn0lnG0-nZfdEvE,743
@@ -1987,7 +1987,7 @@ edc_navbar/migrations/0009_alter_edcpermissions_device_created_and_more.py,sha25
1987
1987
  edc_navbar/migrations/0010_alter_edcpermissions_revision.py,sha256=fbma4ZbSTYMqZug7ZJJ_D2ckFO2A8Koqwp1sn_BxJws,789
1988
1988
  edc_navbar/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1989
1989
  edc_navbar/models.py,sha256=K4enndi4ov-N--3LQWeez3yuCMzmbaVGd7J72q_f73Q,306
1990
- edc_navbar/navbar.py,sha256=zCe1SrgKoVYIQ8JTf6RpX0pRsA_n9dEm_KIKxPkrG_0,1723
1990
+ edc_navbar/navbar.py,sha256=rtB6t9h1etGahyYIFMVLVy_1s9FYL3dfvxUXI73bAJY,1705
1991
1991
  edc_navbar/navbar_item.py,sha256=bCj301HLa1ECzlFm_U1kYw4NPuBlyfWMovQQraI6iB0,1782
1992
1992
  edc_navbar/navbars.py,sha256=gyEgEowvluQheEVfejWzLbog7FwUl-oRu8KvTG3oEO4,1157
1993
1993
  edc_navbar/site_navbars.py,sha256=OcutGzhI2uyEqi8eGhhihId8mmZFRSIFcjpTKPszvyY,4220
@@ -2764,15 +2764,16 @@ edc_qareports/templates/edc_qareports/qa_report_note.html,sha256=FnHwiHvucF90gnQ
2764
2764
  edc_qareports/urls.py,sha256=2QK5ajM8JPflnnp-nPybb9n_SxUZF-tzoW0WpcpFFZs,296
2765
2765
  edc_qareports/utils.py,sha256=yy_gHhLgKabU_lfVTU4QTE1hSBfipy7yny2WD2-dZBE,4058
2766
2766
  edc_randomization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2767
- edc_randomization/admin.py,sha256=zLNApXP04QMFPer3OZhYnDmBrgY8nMM_vKvVkazogZo,5855
2767
+ edc_randomization/admin.py,sha256=g9x71IyTXgcrg8dNL_kJ1lSU2asfC-GqlMv62jofFuM,5939
2768
2768
  edc_randomization/admin_site.py,sha256=FC1NwilwEAiUkD8ZrXARElDHugMrzmfL_CPrKR8_MP8,185
2769
- edc_randomization/apps.py,sha256=UnJPhM6LNP3GGfc4fJxs0LMmwG7LngjF3GzyclTE0qg,755
2770
- edc_randomization/auth_objects.py,sha256=PJQCRGbXXhyDd2MGt4d-VJ65EcO0aE-k9OUzf6PXUYQ,2988
2769
+ edc_randomization/apps.py,sha256=QKnAPbUF_PNkUjadujqWD8FvgQW0HhMV1TCHfHXHCIM,728
2770
+ edc_randomization/auth_objects.py,sha256=wfQZyQNKBcQNs5kVIfmm5enWbn9NPScz4s_IYu63RIM,3200
2771
2771
  edc_randomization/auths.py,sha256=SxhYd1vZkjgblT2_YoabhcLIroi-A0VJ7IExnlFNpr8,860
2772
- edc_randomization/blinding.py,sha256=UB_PGOJ6En49_zwngcRga6SbnCMrNFkoTjlP4FkLQV8,2375
2772
+ edc_randomization/blinding.py,sha256=Lo2XKlBwqobOsw0EoCe3CxU3Byfzy47AdRKiLAeAd-M,2198
2773
2773
  edc_randomization/choices.py,sha256=OZWHp-QqeEoLSr0R4XhJpscPy9k1HCBeyVvWMLB13kk,137
2774
2774
  edc_randomization/constants.py,sha256=WcbX-XPWydixQ6jBwk2rs1xEXAfEvcdBEyU7w5vUtI8,385
2775
- edc_randomization/decorators.py,sha256=ya_wYA8apbsH07xH7MtGNgRCKWzBKAGhI3FJelkD53Y,657
2775
+ edc_randomization/decorators.py,sha256=X9BbD3RZLmbshOLZHRKWg1mM9biNYvC9zoLQLmi7zUE,676
2776
+ edc_randomization/exceptions.py,sha256=l7LWBVWFiMIb1IiWh0PLamazqTVXG4SlvkW28ABVYqA,1141
2776
2777
  edc_randomization/migrations/0001_initial.py,sha256=AIwB1QuiIdnBqLXcJRTTRvR98IRuWuj5LCGhn_xt9kA,6098
2777
2778
  edc_randomization/migrations/0002_historicalrandomizationlist.py,sha256=6ucmdv3E1FoWYvl4XPs5EhQKRIcBkjMiQQS4EXCGMaY,7191
2778
2779
  edc_randomization/migrations/0003_auto_20191024_0426.py,sha256=H0ezbsuy80W5RyilNhWLqGHMjni9mPvHKQrwVdGhzdw,485
@@ -2789,18 +2790,18 @@ edc_randomization/migrations/0013_alter_edcpermissions_revision_and_more.py,sha2
2789
2790
  edc_randomization/migrations/0014_alter_edcpermissions_device_created_and_more.py,sha256=oLNjuC-TkyTZNsos10e1GOisepx4X-r4P4yGnwu-95U,4771
2790
2791
  edc_randomization/migrations/0015_alter_edcpermissions_revision_and_more.py,sha256=cPhN0Lh_MBe7e2CkprclFIyj-110FZ3waZULUeCt-Jg,1806
2791
2792
  edc_randomization/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2792
- edc_randomization/model_mixins.py,sha256=pKLkZJeYgobKPyHVJTby8Q7zBKQoflbLgraTN4pv_Ys,3743
2793
+ edc_randomization/model_mixins.py,sha256=BJe7eLxveTFNPIS7hz7796hSL0DFvTml03HmNih3tQs,3809
2793
2794
  edc_randomization/models/__init__.py,sha256=iaJ6FM7VVqLpp8M-OjVWjohp4ELdxtPXqbEiT3iN5Tw,94
2794
2795
  edc_randomization/models/edc_permissions.py,sha256=f6P4zXxLQdTAbn2KnFlwfE86Lmzj7Ecp4JJ2F8mrwTc,286
2795
2796
  edc_randomization/models/randomization_list.py,sha256=Hd5yUSZ5HBKhNzYdlbJufab8U2x3Dzw8n4WXnYzJxyc,253
2796
- edc_randomization/randomization_list_importer.py,sha256=-rFYIyTY2NZHzTJ9KH5bvA3PecNoZE2MSz4IUxszoHU,12739
2797
- edc_randomization/randomization_list_verifier.py,sha256=xeybFaxW62Zq0wW36CThU_E40BPxkrYpItCRwuMcLAI,6682
2798
- edc_randomization/randomizer.py,sha256=DU4iH8mMUXiE7UpAiKHOrJXvhdWkjTTG9ex8Ho0xIHo,12940
2797
+ edc_randomization/randomization_list_importer.py,sha256=YZG5Z0faOD85UpIFLkwt9Hq-2IAyI43xGINdhzI5YKQ,12815
2798
+ edc_randomization/randomization_list_verifier.py,sha256=A0LRQkI-pMt9TaJ3kps5LncMJZaz5eMdRFDeI35fIGo,6918
2799
+ edc_randomization/randomizer.py,sha256=_LYZp7gmpOIkovCs7TfAt78wb_Kl4iV6I85YIxgQ5pg,12729
2799
2800
  edc_randomization/randomizers.py,sha256=JvMDAnsgvRZkl50Bawm2iLsyw3bfWfGwBafn54K5g44,297
2800
- edc_randomization/site_randomizers.py,sha256=et5Yf92ld8qgn-AThrBTRALB3WInI03UK4yGw2PlsKo,4499
2801
- edc_randomization/system_checks.py,sha256=S_2Vkq3J3DQVEXOV3JNNlCzuZcl1l34-tTnaBaSZaBk,3025
2801
+ edc_randomization/site_randomizers.py,sha256=CcHRz_J6OlJ64-iNul_LOHM0ai6SbcX5AMDrFFMewDA,4390
2802
+ edc_randomization/system_checks.py,sha256=ryGqND9mS0u05MSU-wBBfX-HaOdoFMdWok25uM2Mcdo,3039
2802
2803
  edc_randomization/urls.py,sha256=yQnOoy5U5uL00f7_JJcZk7toskmZunG_kJdl-epCV9U,223
2803
- edc_randomization/utils.py,sha256=uRTtoyAuWo5D6y65ZHBWMSody8IMJBK6cS3mFmJGIMw,5730
2804
+ edc_randomization/utils.py,sha256=Os5YtrI4RDGnudmE5nuTxXiuBWqekObipWfF79etkmE,5697
2804
2805
  edc_randomization/views.py,sha256=mQnAoYzIbqXFXablJbPY3NdJc4K3fD4le-GOF2kpMmE,340
2805
2806
  edc_refusal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2806
2807
  edc_refusal/admin.py,sha256=upce4HEPyPYKTe_R_krQ9TaLufcJN50c0lw4JrzbTwE,2151
@@ -3406,7 +3407,7 @@ edc_vitals/models/fields/waist_circumference.py,sha256=fZcHFDdEwWLjIVLktKrFCD9UU
3406
3407
  edc_vitals/models/fields/weight.py,sha256=zo9_9e3Cpu0UqoRbWS-iDkcDo6fK80b1dDQy4x4MyxE,921
3407
3408
  edc_vitals/utils.py,sha256=vXid44KUXxeaSyund_y5MNXc5DFJs052_PwUAjszE2k,1384
3408
3409
  edc_vitals/validators.py,sha256=vNiElWMs0rRnHRNuVoPLRf0rW_C_0xcfUyep1Y_Si5s,156
3409
- clinicedc-2.0.25.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
3410
- clinicedc-2.0.25.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
3411
- clinicedc-2.0.25.dist-info/METADATA,sha256=40tXyAKH80VvRdH6fqf00N2gLL4ZUo2-hj11Uwem5x8,15787
3412
- clinicedc-2.0.25.dist-info/RECORD,,
3410
+ clinicedc-2.0.27.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
3411
+ clinicedc-2.0.27.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
3412
+ clinicedc-2.0.27.dist-info/METADATA,sha256=8L752Me7nR7AH9xGK6t20ujMHs_CD8Tkb2cUPIURzFo,15787
3413
+ clinicedc-2.0.27.dist-info/RECORD,,
edc_constants/choices.py CHANGED
@@ -4,6 +4,7 @@ from .constants import (
4
4
  ABNORMAL,
5
5
  ABSENT,
6
6
  AFTERNOON,
7
+ AGREE,
7
8
  ALIVE,
8
9
  ALWAYS,
9
10
  ANYTIME,
@@ -11,8 +12,11 @@ from .constants import (
11
12
  COMPLETE,
12
13
  DEAD,
13
14
  DECLINED,
15
+ DIFFICULT,
16
+ DISAGREE,
14
17
  DONT_KNOW,
15
18
  DWTA,
19
+ EASY,
16
20
  EVENING,
17
21
  FALSE,
18
22
  FASTING,
@@ -26,6 +30,7 @@ from .constants import (
26
30
  MORNING,
27
31
  NAIVE,
28
32
  NEG,
33
+ NEUTRAL,
29
34
  NEVER,
30
35
  NO,
31
36
  NON_FASTING,
@@ -51,9 +56,13 @@ from .constants import (
51
56
  REFUSED,
52
57
  SMOKER,
53
58
  SOMETIMES,
59
+ STRONGLY_AGREE,
60
+ STRONGLY_DISAGREE,
54
61
  TBD,
55
62
  TRUE,
56
63
  UNKNOWN,
64
+ VERY_DIFFICULT,
65
+ VERY_EASY,
57
66
  VERY_OFTEN,
58
67
  WEEKDAYS,
59
68
  WEEKENDS,
@@ -833,3 +842,20 @@ WHYNOPARTICIPATE_CHOICE = (
833
842
  ("OTHER", _("Other, specify:")),
834
843
  ("not_answering", _("Don't want to answer")),
835
844
  )
845
+
846
+
847
+ DISAGREE_TO_AGREE_CHOICE = (
848
+ (STRONGLY_DISAGREE, _("Strongly disagree")),
849
+ (DISAGREE, _("disagree")),
850
+ (NEUTRAL, _("Neutral")),
851
+ (AGREE, _("Agree")),
852
+ (STRONGLY_AGREE, _("Strongly agree")),
853
+ )
854
+
855
+ DIFFICULT_TO_EASY_CHOICE = (
856
+ (VERY_DIFFICULT, _("Very difficult")),
857
+ (DIFFICULT, _("Difficult")),
858
+ (NEUTRAL, _("Neutral")),
859
+ (EASY, _("Easy")),
860
+ (VERY_EASY, _("Very easy")),
861
+ )
@@ -8,6 +8,7 @@ ABNORMAL = "ABNORMAL"
8
8
  ABSENT = "absent"
9
9
  ADDITIONAL = True
10
10
  AFTERNOON = "afternoon"
11
+ AGREE = "agree"
11
12
  ALIVE = "alive"
12
13
  ALWAYS = "always"
13
14
  ANONYMOUS = "anonymous"
@@ -40,6 +41,7 @@ DEFAULTER = "defaulter"
40
41
  DELETE = "DELETE"
41
42
  DELIVERY = "delivery"
42
43
  DIABETES = "diabetes"
44
+ DISAGREE = "disagree"
43
45
  DIVORCED = "divorced"
44
46
  DM = "dm"
45
47
  DONE = "done"
@@ -109,6 +111,7 @@ MULTI_MORBIDITY = "multi"
109
111
  NAIVE = "NAIVE"
110
112
  NCD = "NCD"
111
113
  NEG = "NEG"
114
+ NEUTRAL = "neutral"
112
115
  NEVER = "NEVER"
113
116
  NEW = "New"
114
117
  NEW_FIELD = "new_field"
@@ -192,6 +195,8 @@ SMOKER = "smoker"
192
195
  SOMETIMES = "sometimes"
193
196
  STEROIDS = "steroids"
194
197
  STOPPED = "stopped"
198
+ STRONGLY_AGREE = "strongly_agree"
199
+ STRONGLY_DISAGREE = "strongly_disagree"
195
200
  STUDY_DEFINED_TIMEPOINT = "study_defined_timepoint"
196
201
  SUBJECT = "subject"
197
202
  SYSTEMATIC = "systematic"
@@ -238,3 +243,8 @@ THURSDAY = "thursday"
238
243
  FRIDAY = "friday"
239
244
  SATURDAY = "saturday"
240
245
  SUNDAY = "sunday"
246
+
247
+ VERY_DIFFICULT = "very_difficult"
248
+ DIFFICULT = "difficult"
249
+ EASY = "easy"
250
+ VERY_EASY = "very_easy"
edc_navbar/navbar.py CHANGED
@@ -29,7 +29,7 @@ class Navbar:
29
29
 
30
30
  def get(self, name: str) -> NavbarItem | None:
31
31
  try:
32
- navbar_item = [nb for nb in self.navbar_items if nb.name == name][0]
32
+ navbar_item = next(nb for nb in self.navbar_items if nb.name == name)
33
33
  except IndexError:
34
34
  navbar_item = None
35
35
  return navbar_item
@@ -37,7 +37,7 @@ class Navbar:
37
37
  def set_active(self, name: str) -> None:
38
38
  if name:
39
39
  for navbar_item in self.navbar_items:
40
- navbar_item.active = True if navbar_item.name == name else False
40
+ navbar_item.active = navbar_item.name == name
41
41
 
42
42
  def show_user_permissions(self, user: User = None) -> dict[str, dict[str, bool]]:
43
43
  """Returns the permissions required to access this Navbar
@@ -17,6 +17,8 @@ from .auth_objects import RANDO_UNBLINDED
17
17
  from .blinding import user_is_blinded
18
18
  from .site_randomizers import site_randomizers
19
19
 
20
+ __all__ = ["RandomizationListModelAdmin", "print_pharmacy_labels"]
21
+
20
22
 
21
23
  @admin.action(permissions=["view"], description="Print labels for pharmacy")
22
24
  def print_pharmacy_labels(modeladmin, request, queryset):
@@ -59,7 +61,7 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
59
61
 
60
62
  search_fields = ("subject_identifier", "sid")
61
63
 
62
- def get_fieldsets(self, request, obj=None):
64
+ def get_fieldsets(self, request, obj=None): # noqa: ARG002
63
65
  return (
64
66
  (None, {"fields": self.get_fieldnames(request)}),
65
67
  audit_fieldset_tuple,
edc_randomization/apps.py CHANGED
@@ -1,8 +1,4 @@
1
- import os
2
- from warnings import warn
3
-
4
1
  from django.apps import AppConfig as DjangoAppConfig
5
- from django.conf import settings
6
2
  from django.core.checks.registry import register
7
3
 
8
4
  from .system_checks import randomizationlist_check
@@ -17,10 +13,11 @@ class AppConfig(DjangoAppConfig):
17
13
  def ready(self):
18
14
  register(randomizationlist_check, deploy=True)
19
15
 
20
- @property
21
- def randomization_list_path(self):
22
- warn(
23
- "Use of settings.RANDOMIZATION_LIST_PATH has been deprecated. "
24
- "See site_randomizers in edc_randomization"
25
- )
26
- return os.path.join(settings.RANDOMIZATION_LIST_PATH)
16
+ # @property
17
+ # def randomization_list_path(self):
18
+ # warn(
19
+ # "Use of settings.RANDOMIZATION_LIST_PATH has been deprecated. "
20
+ # "See site_randomizers in edc_randomization",
21
+ # stacklevel=2,
22
+ # )
23
+ # return os.path.join(settings.RANDOMIZATION_LIST_PATH)
@@ -2,6 +2,15 @@ import sys
2
2
 
3
3
  from .site_randomizers import site_randomizers
4
4
 
5
+ __all__ = [
6
+ "RANDO_BLINDED",
7
+ "RANDO_UNBLINDED",
8
+ "get_rando_permissions_codenames",
9
+ "get_rando_permissions_tuples",
10
+ "make_randomizationlist_view_only",
11
+ "update_rando_group_permissions",
12
+ ]
13
+
5
14
  RANDO_UNBLINDED = "RANDO_UNBLINDED"
6
15
  RANDO_BLINDED = "RANDO_BLINDED"
7
16
 
@@ -4,7 +4,6 @@ from django import forms
4
4
  from django.conf import settings
5
5
  from django.contrib.auth import get_user_model
6
6
  from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
7
- from django.utils.html import format_html
8
7
  from django.utils.safestring import mark_safe
9
8
 
10
9
  from .auth_objects import RANDO_UNBLINDED
@@ -43,13 +42,11 @@ def user_is_blinded(username) -> bool:
43
42
  return blinded
44
43
 
45
44
 
46
- def user_is_blinded_from_request(request):
47
- if user_is_blinded(request.user.username) or (
45
+ def user_is_blinded_from_request(request) -> bool:
46
+ return user_is_blinded(request.user.username) or (
48
47
  not user_is_blinded(request.user.username)
49
48
  and RANDO_UNBLINDED not in [g.name for g in request.user.groups.all()]
50
- ):
51
- return True
52
- return False
49
+ )
53
50
 
54
51
 
55
52
  def raise_if_prohibited_from_unblinded_rando_group(username: str, groups: Iterable) -> None:
@@ -61,12 +58,9 @@ def raise_if_prohibited_from_unblinded_rando_group(username: str, groups: Iterab
61
58
  if RANDO_UNBLINDED in [grp.name for grp in groups] and user_is_blinded(username):
62
59
  raise forms.ValidationError(
63
60
  {
64
- "groups": format_html(
65
- "{}",
66
- mark_safe(
67
- "This user is not unblinded and may not added "
68
- "to the <U>RANDO_UNBLINDED</U> group."
69
- ), # nosec B703 B308
61
+ "groups": mark_safe(
62
+ "This user is not unblinded and may not added "
63
+ "to the <U>RANDO_UNBLINDED</U> group."
70
64
  )
71
65
  }
72
66
  )
@@ -1,11 +1,10 @@
1
1
  from typing import Any
2
2
 
3
+ from .exceptions import RegisterRandomizerError
3
4
  from .randomizer import Randomizer
4
5
  from .site_randomizers import site_randomizers
5
6
 
6
-
7
- class RegisterRandomizerError(Exception):
8
- pass
7
+ __all__ = ["register"]
9
8
 
10
9
 
11
10
  def register(site=None, **kwargs) -> Any:
@@ -0,0 +1,73 @@
1
+ from django.core.exceptions import ValidationError
2
+
3
+
4
+ class InvalidAssignment(Exception): # noqa: N818
5
+ pass
6
+
7
+
8
+ class RandomizationListImportError(Exception):
9
+ pass
10
+
11
+
12
+ class RandomizationListAlreadyImported(Exception): # noqa: N818
13
+ pass
14
+
15
+
16
+ class RandomizationListError(Exception):
17
+ pass
18
+
19
+
20
+ class RegistryNotLoaded(Exception): # noqa: N818
21
+ pass
22
+
23
+
24
+ class NotRegistered(Exception): # noqa: N818
25
+ pass
26
+
27
+
28
+ class AlreadyRegistered(Exception): # noqa: N818
29
+ pass
30
+
31
+
32
+ class SiteRandomizerError(Exception):
33
+ pass
34
+
35
+
36
+ class RandomizationListExporterError(Exception):
37
+ pass
38
+
39
+
40
+ class SubjectNotRandomization(Exception): # noqa: N818
41
+ pass
42
+
43
+
44
+ class InvalidAssignmentDescriptionMap(Exception): # noqa: N818
45
+ pass
46
+
47
+
48
+ class RandomizationListFileNotFound(Exception): # noqa: N818
49
+ pass
50
+
51
+
52
+ class RandomizationListNotLoaded(Exception): # noqa: N818
53
+ pass
54
+
55
+
56
+ class RandomizationError(Exception):
57
+ pass
58
+
59
+
60
+ class AlreadyRandomized(ValidationError): # noqa: N818
61
+ pass
62
+
63
+
64
+ class AllocationError(Exception):
65
+ pass
66
+
67
+
68
+ class RegisterRandomizerError(Exception):
69
+ pass
70
+
71
+
72
+ class RandomizationListModelError(Exception):
73
+ pass
@@ -7,12 +7,11 @@ from django_crypto_fields.fields import EncryptedCharField
7
7
  from edc_model.models import HistoricalRecords
8
8
  from edc_sites.managers import CurrentSiteManager
9
9
 
10
+ from .exceptions import RandomizationListModelError
10
11
  from .randomizer import RandomizationError
11
12
  from .site_randomizers import site_randomizers
12
13
 
13
-
14
- class RandomizationListModelError(Exception):
15
- pass
14
+ __all__ = ["RandomizationListManager", "RandomizationListModelMixin"]
16
15
 
17
16
 
18
17
  class RandomizationListManager(models.Manager):
@@ -12,21 +12,16 @@ from tqdm import tqdm
12
12
 
13
13
  from edc_sites.site import sites as site_sites
14
14
 
15
+ from .exceptions import (
16
+ InvalidAssignment,
17
+ RandomizationListAlreadyImported,
18
+ RandomizationListImportError,
19
+ )
15
20
  from .randomization_list_verifier import RandomizationListVerifier
16
21
 
17
22
  style = color_style()
18
23
 
19
-
20
- class RandomizationListImportError(Exception):
21
- pass
22
-
23
-
24
- class RandomizationListAlreadyImported(Exception):
25
- pass
26
-
27
-
28
- class InvalidAssignment(Exception):
29
- pass
24
+ __all__ = ["RandomizationListImporter"]
30
25
 
31
26
 
32
27
  class RandomizationListImporter:
@@ -157,7 +152,7 @@ class RandomizationListImporter:
157
152
  index = 0
158
153
  with self.randomizationlist_path.open(mode="r") as csvfile:
159
154
  reader = csv.DictReader(csvfile)
160
- for index, row in enumerate(reader):
155
+ for index, _ in enumerate(reader):
161
156
  if index == 0:
162
157
  continue
163
158
  if index == 0:
@@ -179,11 +174,11 @@ class RandomizationListImporter:
179
174
  elif index == 1:
180
175
  if self.dryrun:
181
176
  row_as_dict = {k: v for k, v in row.items()}
182
- print(" --> First row:")
183
- print(f" --> {list(row_as_dict.keys())}")
184
- print(f" --> {list(row_as_dict.values())}")
177
+ sys.stdout.write(" --> First row:\n")
178
+ sys.stdout.write(f" --> {list(row_as_dict.keys())}\n")
179
+ sys.stdout.write(f" --> {list(row_as_dict.values())}\n")
185
180
  obj = self.randomizer_model_cls(**self.get_import_options(row))
186
- pprint(obj.__dict__)
181
+ pprint(obj.__dict__) # noqa: T203
187
182
  else:
188
183
  break
189
184
 
@@ -304,7 +299,7 @@ class RandomizationListImporter:
304
299
  **self.get_extra_import_options(row),
305
300
  )
306
301
 
307
- def get_extra_import_options(self, row):
302
+ def get_extra_import_options(self, row): # noqa: ARG002
308
303
  return {}
309
304
 
310
305
  @staticmethod
@@ -318,9 +313,9 @@ class RandomizationListImporter:
318
313
  """Returns the site name or raises"""
319
314
  try:
320
315
  site_name = self.get_site_names()[row["site_name"].lower()]
321
- except KeyError:
316
+ except KeyError as e:
322
317
  raise RandomizationListImportError(
323
318
  f"Invalid site. Got {row['site_name']}. "
324
319
  f"Expected one of {self.get_site_names().keys()}"
325
- )
320
+ ) from e
326
321
  return site_name
@@ -6,17 +6,12 @@ from django.core.exceptions import ObjectDoesNotExist
6
6
  from django.core.management.color import color_style
7
7
  from django.db.utils import OperationalError, ProgrammingError
8
8
 
9
+ from .exceptions import InvalidAssignment, RandomizationListError
9
10
  from .site_randomizers import site_randomizers
10
11
 
11
12
  style = color_style()
12
13
 
13
-
14
- class RandomizationListError(Exception):
15
- pass
16
-
17
-
18
- class InvalidAssignment(Exception):
19
- pass
14
+ __all__ = ["RandomizationListVerifier"]
20
15
 
21
16
 
22
17
  class RandomizationListVerifier:
@@ -78,28 +73,31 @@ class RandomizationListVerifier:
78
73
  def verify(self) -> str | None:
79
74
  message = None
80
75
 
76
+ # read and sort from CSV file
81
77
  with self.randomizationlist_path.open(mode="r") as f:
82
78
  reader = csv.DictReader(f)
83
79
  all_rows = [{k: v.strip() for k, v in row.items() if k} for row in reader]
84
80
  sorted_rows = sorted(
85
- all_rows, key=lambda row: (row.get("site_name", ""), row.get("sid", ""))
81
+ all_rows, key=lambda row: (row.get("site_name", ""), int(row.get("sid", 0)))
86
82
  )
87
83
 
88
- for index, row in enumerate(sorted_rows, start=1):
89
- sys.stdout.write(f"Index: {index}, SID: {row.get('sid')}, Row: {row}\n")
90
- message = self.inspect_row(index - 1, row)
91
- if message:
92
- break
93
- if self.sid_count_for_tests and index == self.sid_count_for_tests:
94
- break
95
-
96
- if not message and self.count != index:
84
+ # compare sorted CSV length with DB
85
+ if len(sorted_rows) != self.randomizer_model_cls.objects.all().count():
86
+ expected_cnt = len(sorted_rows)
87
+ actual_cnt = self.randomizer_model_cls.objects.all().count()
97
88
  message = (
98
- f"Randomization list count is off. Expected {index} (CSV). "
99
- f"Got {self.count} (model_cls). See file "
89
+ f"Randomization list count is off. Expected {expected_cnt} (CSV). "
90
+ f"Got {actual_cnt} (model_cls). See file "
100
91
  f"{self.randomizationlist_path}. "
101
92
  f"Resolve this issue before using the system."
102
93
  )
94
+ if not message:
95
+ # compare sorted CSV data to DB
96
+ for index, row in enumerate(sorted_rows, start=1):
97
+ sys.stdout.write(f"Index: {index}, SID: {row.get('sid')}, Row: {row}\n")
98
+ message = self.inspect_row(index - 1, row)
99
+ if message:
100
+ break
103
101
  return message
104
102
 
105
103
  def inspect_row(self, index: int, row) -> str | None:
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from django.apps import apps as django_apps
10
10
  from django.conf import settings
11
- from django.core.exceptions import ObjectDoesNotExist, ValidationError
11
+ from django.core.exceptions import ObjectDoesNotExist
12
12
  from django.db.models import Q
13
13
 
14
14
  from edc_registration.utils import get_registered_subject_model_cls
@@ -18,38 +18,22 @@ from .constants import (
18
18
  DEFAULT_ASSIGNMENT_MAP,
19
19
  RANDOMIZED,
20
20
  )
21
- from .randomization_list_importer import (
21
+ from .exceptions import (
22
+ AllocationError,
23
+ AlreadyRandomized,
24
+ InvalidAssignmentDescriptionMap,
25
+ RandomizationError,
22
26
  RandomizationListAlreadyImported,
23
- RandomizationListImporter,
27
+ RandomizationListFileNotFound,
24
28
  )
29
+ from .randomization_list_importer import RandomizationListImporter
25
30
  from .utils import get_randomization_list_path
26
31
 
27
32
  if TYPE_CHECKING:
28
33
  from edc_registration.models import RegisteredSubject
29
34
 
30
35
 
31
- class InvalidAssignmentDescriptionMap(Exception): # noqa: N818
32
- pass
33
-
34
-
35
- class RandomizationListFileNotFound(Exception): # noqa: N818
36
- pass
37
-
38
-
39
- class RandomizationListNotLoaded(Exception): # noqa: N818
40
- pass
41
-
42
-
43
- class RandomizationError(Exception):
44
- pass
45
-
46
-
47
- class AlreadyRandomized(ValidationError): # noqa: N818
48
- pass
49
-
50
-
51
- class AllocationError(Exception):
52
- pass
36
+ __all__ = ["Randomizer"]
53
37
 
54
38
 
55
39
  class Randomizer:
@@ -61,8 +45,8 @@ class Randomizer:
61
45
  `site_randomizer` by default. To prevent registration set
62
46
  settings.EDC_RANDOMIZATION_REGISTER_DEFAULT_RANDOMIZER=False.
63
47
 
64
- assignment_map: {<assignment:str)>: <allocation:int>, ...}
65
- assignment_description_map: {<assignment:str)>: <description:str>, ...}
48
+ assignment_map: {<assignment:str>: <allocation:int>, ...}
49
+ assignment_description_map: {<assignment:str>: <description:str>, ...}
66
50
 
67
51
 
68
52
  Usage:
@@ -7,26 +7,12 @@ from typing import TYPE_CHECKING
7
7
  from django.apps import apps as django_apps
8
8
  from django.utils.module_loading import import_module, module_has_submodule
9
9
 
10
+ from .exceptions import AlreadyRegistered, NotRegistered, RegistryNotLoaded
11
+
10
12
  if TYPE_CHECKING:
11
13
  from edc_randomization.randomizer import Randomizer
12
14
 
13
15
 
14
- class RegistryNotLoaded(Exception):
15
- pass
16
-
17
-
18
- class NotRegistered(Exception):
19
- pass
20
-
21
-
22
- class AlreadyRegistered(Exception):
23
- pass
24
-
25
-
26
- class SiteRandomizerError(Exception):
27
- pass
28
-
29
-
30
16
  class SiteRandomizers:
31
17
  """Main controller of :class:`SiteRandomizers` objects.
32
18
 
@@ -3,7 +3,7 @@ import sys
3
3
  from dataclasses import dataclass
4
4
 
5
5
  from django.conf import settings
6
- from django.core.checks import Error, Warning
6
+ from django.core.checks import Error, Warning # noqa: A004
7
7
  from django.core.exceptions import ImproperlyConfigured
8
8
  from django.core.management import color_style
9
9
 
@@ -14,17 +14,10 @@ from django_pandas.io import read_frame
14
14
  from edc_pdutils.constants import SYSTEM_COLUMNS
15
15
  from edc_sites.site import sites
16
16
 
17
+ from .exceptions import RandomizationListExporterError, SubjectNotRandomization
17
18
  from .site_randomizers import site_randomizers
18
19
 
19
20
 
20
- class RandomizationListExporterError(Exception):
21
- pass
22
-
23
-
24
- class SubjectNotRandomization(Exception): # noqa: N818
25
- pass
26
-
27
-
28
21
  def get_randomization_list_path() -> Path:
29
22
  return Path(
30
23
  getattr(
@@ -149,7 +142,7 @@ def generate_fake_randomization_list(
149
142
 
150
143
  def export_randomization_list(
151
144
  randomizer_name: str, path: str | None = None, username: str | None = None
152
- ):
145
+ ) -> Path:
153
146
  randomizer_cls = site_randomizers.get(randomizer_name)
154
147
 
155
148
  try:
@@ -184,4 +177,4 @@ def export_randomization_list(
184
177
  )
185
178
  df.to_csv(**opts)
186
179
  sys.stdout.write(f"{filename!s}\n")
187
- return filename
180
+ return Path(filename)