clinicedc 2.0.26__py3-none-any.whl → 2.0.28__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.26
3
+ Version: 2.0.28
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
@@ -593,9 +593,9 @@ edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_mixin.py,
593
593
  edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py,sha256=Wwo3l-pdFlLkK8q50GkaCyPUXtuPJXODqYfb3HF1jDw,9613
594
594
  edc_consent/modelform_mixins/consent_modelform_mixin/review_fields_modelform_mixin.py,sha256=HOiWpf8SnPaGMtRbKM6uAglbV1HmQWdzxSoyQW1ab2U,1922
595
595
  edc_consent/modelform_mixins/requires_consent_modelform_mixin.py,sha256=-rQAmOvl1fx8-lEUB6M4V7tqaTqDRxtid002EWYPIxM,2340
596
- edc_consent/models/__init__.py,sha256=hBdNWnac5MtlZATkOKZHUelMRn2M6jPCgGktCio5Wgk,282
596
+ edc_consent/models/__init__.py,sha256=kTUrf6WlsKInX7jVuD3DyIqRW1OUaeSemflLLzWLV_I,278
597
597
  edc_consent/models/edc_permissions.py,sha256=f9PWQ1ZFz4rKT7wFpfr8anQ7mV6sCdNaWNMimPLGae8,338
598
- edc_consent/models/signals.py,sha256=vhLiZPwSinm_CXh-r9Ty_Xaja5okvqrSFCkDp6DHylc,3020
598
+ edc_consent/models/signals.py,sha256=kjYIlFTcm395_z4h3wlBkYbcxzAjgqsea3_ik5rm6CM,3320
599
599
  edc_consent/navbars.py,sha256=Bqb2UfOCLL6yuT_fSvKYQymbBj7V6q9CwvmI-VdUmls,334
600
600
  edc_consent/site_consents.py,sha256=jQYufDBievxoyOlC0Q1Llxv9xYqCEyk58Q1Fg_cu3mY,16882
601
601
  edc_consent/stubs.py,sha256=ZXjNqT6nBHRFHCB5tCOzBC3ZdiiBR3IitygO3FldTZA,513
@@ -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
@@ -2794,8 +2794,8 @@ edc_randomization/model_mixins.py,sha256=BJe7eLxveTFNPIS7hz7796hSL0DFvTml03HmNih
2794
2794
  edc_randomization/models/__init__.py,sha256=iaJ6FM7VVqLpp8M-OjVWjohp4ELdxtPXqbEiT3iN5Tw,94
2795
2795
  edc_randomization/models/edc_permissions.py,sha256=f6P4zXxLQdTAbn2KnFlwfE86Lmzj7Ecp4JJ2F8mrwTc,286
2796
2796
  edc_randomization/models/randomization_list.py,sha256=Hd5yUSZ5HBKhNzYdlbJufab8U2x3Dzw8n4WXnYzJxyc,253
2797
- edc_randomization/randomization_list_importer.py,sha256=YZG5Z0faOD85UpIFLkwt9Hq-2IAyI43xGINdhzI5YKQ,12815
2798
- edc_randomization/randomization_list_verifier.py,sha256=A0LRQkI-pMt9TaJ3kps5LncMJZaz5eMdRFDeI35fIGo,6918
2797
+ edc_randomization/randomization_list_importer.py,sha256=FzL7-Iq9GJEXoi_ggGKvk7fApfL_oxcI7eJJUXyc4GM,13076
2798
+ edc_randomization/randomization_list_verifier.py,sha256=o-niIHR6OapiAXbGeLzW03PVz5DdYKOidJIcWJwCalM,7069
2799
2799
  edc_randomization/randomizer.py,sha256=_LYZp7gmpOIkovCs7TfAt78wb_Kl4iV6I85YIxgQ5pg,12729
2800
2800
  edc_randomization/randomizers.py,sha256=JvMDAnsgvRZkl50Bawm2iLsyw3bfWfGwBafn54K5g44,297
2801
2801
  edc_randomization/site_randomizers.py,sha256=CcHRz_J6OlJ64-iNul_LOHM0ai6SbcX5AMDrFFMewDA,4390
@@ -3292,7 +3292,7 @@ edc_visit_schedule/schedule/schedule.py,sha256=ROUNin-k1oJVtx_wEXwlj5xrhTw3I_6Ve
3292
3292
  edc_visit_schedule/schedule/visit_collection.py,sha256=9OJKrLtx-H0eL9oo0dsRqYGPICiAMqEF4xIhhlinx0A,2033
3293
3293
  edc_visit_schedule/schedule/window.py,sha256=gsXW7z3P84h6kWX7e7CABmalyLGgbbIwe0Q6atIbyEI,5229
3294
3294
  edc_visit_schedule/simple_model_validator.py,sha256=5lMyVrrlAy4HxLmAqM3he7D2ssCJoyjAWDWnFmJwRT0,789
3295
- edc_visit_schedule/site_visit_schedules.py,sha256=MeeJjyYludPlmjrYHmnx5D6t85DZrWvPyFMgidYbewE,13087
3295
+ edc_visit_schedule/site_visit_schedules.py,sha256=dtFDEWTXQWx1KTFJQQDh2Uv52Ht2Rq64QktkYTsGBIQ,12949
3296
3296
  edc_visit_schedule/subject_schedule.py,sha256=gai8sQ9nek3KEu27GL656twm0zKP3I3g510effNdIT8,16552
3297
3297
  edc_visit_schedule/system_checks.py,sha256=Gp5DXTThRCFXyZ3dlN2S8FJPz16weonpjedcy6XIIms,9300
3298
3298
  edc_visit_schedule/templates/edc_visit_schedule/home.html,sha256=RqtGsq6-zXi43jU0iSv24-xsIXXHkuiHjJvF-yqDyjA,1112
@@ -3407,7 +3407,7 @@ edc_vitals/models/fields/waist_circumference.py,sha256=fZcHFDdEwWLjIVLktKrFCD9UU
3407
3407
  edc_vitals/models/fields/weight.py,sha256=zo9_9e3Cpu0UqoRbWS-iDkcDo6fK80b1dDQy4x4MyxE,921
3408
3408
  edc_vitals/utils.py,sha256=vXid44KUXxeaSyund_y5MNXc5DFJs052_PwUAjszE2k,1384
3409
3409
  edc_vitals/validators.py,sha256=vNiElWMs0rRnHRNuVoPLRf0rW_C_0xcfUyep1Y_Si5s,156
3410
- clinicedc-2.0.26.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
3411
- clinicedc-2.0.26.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
3412
- clinicedc-2.0.26.dist-info/METADATA,sha256=GB5NgaBjnahJvCN28GDKHj2QxuoAECGn_cC7hmIs9cU,15787
3413
- clinicedc-2.0.26.dist-info/RECORD,,
3410
+ clinicedc-2.0.28.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
3411
+ clinicedc-2.0.28.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
3412
+ clinicedc-2.0.28.dist-info/METADATA,sha256=YUfHfAvlHlkygsCSkKKCoV_MMwqlVWaUKE72Gq2R6iA,15787
3413
+ clinicedc-2.0.28.dist-info/RECORD,,
@@ -1,11 +1,11 @@
1
1
  from .edc_permissions import EdcPermissions
2
2
  from .signals import (
3
3
  requires_consent_on_pre_save,
4
- # update_appointment_from_consentext_post_save,
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
- # "update_appointment_from_consentext_post_save",
10
+ "update_appointment_from_consentext_post_save",
11
11
  ]
@@ -1,9 +1,14 @@
1
- from django.db.models.signals import pre_save
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
- # @receiver(
54
- # post_save,
55
- # weak=False,
56
- # dispatch_uid="update_appointment_from_consentext_post_save",
57
- # )
58
- # def update_appointment_from_consentext_post_save(
59
- # sender, instance, raw, created, using, **kwargs
60
- # ):
61
- # if (
62
- # not raw
63
- # and not kwargs.get("update_fields")
64
- # and isinstance(instance, (ConsentExtensionModelMixin,))
65
- # ):
66
- # cdef = site_consents.get_consent_definition(
67
- # model=instance.subject_consent._meta.label_lower,
68
- # version=instance.subject_consent.version,
69
- # )
70
- # visit_schedule, schedule = site_visit_schedules.get_by_consent_definition(cdef)
71
- # subject_schedule = SubjectSchedule(
72
- # instance.subject_consent.subject_identifier,
73
- # visit_schedule=visit_schedule,
74
- # schedule=schedule,
75
- # )
76
- # subject_schedule.refresh_appointments()
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()
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"
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import csv
4
4
  import sys
5
+ from itertools import islice
5
6
  from pathlib import Path
6
7
  from pprint import pprint
7
8
  from uuid import uuid4
@@ -72,6 +73,7 @@ class RandomizationListImporter:
72
73
  username: str | None = None,
73
74
  revision: str | None = None,
74
75
  sid_count_for_tests: int | None = None,
76
+ skip_verify: bool | None = None,
75
77
  extra_csv_fieldnames: tuple[str] | None = None,
76
78
  **kwargs, # noqa: ARG002
77
79
  ):
@@ -84,6 +86,7 @@ class RandomizationListImporter:
84
86
  self.revision = revision
85
87
  self.user = username
86
88
  self.sid_count_for_tests = sid_count_for_tests
89
+ self.skip_verify = skip_verify
87
90
  self.randomizer_model_cls = randomizer_model_cls
88
91
  self.randomizer_name = randomizer_name
89
92
  self.assignment_map = assignment_map
@@ -120,8 +123,9 @@ class RandomizationListImporter:
120
123
  )
121
124
  )
122
125
  rec_count = self._import_csv_to_model()
123
- self.verify_messages = self._verify_data(**kwargs)
124
- self._summarize_results()
126
+ if not self.skip_verify:
127
+ self.verify_messages = self._verify_data(**kwargs)
128
+ self._summarize_results()
125
129
  if self.verbose:
126
130
  sys.stdout.write(
127
131
  style.SUCCESS("\nDone.------------------------------------------------\n")
@@ -221,13 +225,15 @@ class RandomizationListImporter:
221
225
  sid_count = len(self.get_sid_list())
222
226
  with self.randomizationlist_path.open(mode="r") as f:
223
227
  reader = csv.DictReader(f)
228
+ if self.sid_count_for_tests:
229
+ reader = islice(reader, self.sid_count_for_tests)
224
230
  all_rows = [{k: v.strip() for k, v in row.items() if k} for row in reader]
225
231
  sorted_rows = sorted(
226
232
  all_rows, key=lambda row: (row.get("site_name", ""), row.get("sid", ""))
227
233
  )
228
234
  for row in tqdm(sorted_rows, total=sid_count):
229
- if self.sid_count_for_tests and len(objs) == self.sid_count_for_tests:
230
- break
235
+ # if self.sid_count_for_tests and len(objs) == self.sid_count_for_tests:
236
+ # break
231
237
  try:
232
238
  self.randomizer_model_cls.objects.get(sid=row["sid"])
233
239
  except ObjectDoesNotExist:
@@ -1,5 +1,6 @@
1
1
  import csv
2
2
  import sys
3
+ from itertools import islice
3
4
  from pathlib import Path
4
5
 
5
6
  from django.core.exceptions import ObjectDoesNotExist
@@ -72,15 +73,16 @@ class RandomizationListVerifier:
72
73
 
73
74
  def verify(self) -> str | None:
74
75
  message = None
75
-
76
76
  # read and sort from CSV file
77
77
  with self.randomizationlist_path.open(mode="r") as f:
78
78
  reader = csv.DictReader(f)
79
+ if self.sid_count_for_tests:
80
+ reader = islice(reader, self.sid_count_for_tests)
79
81
  all_rows = [{k: v.strip() for k, v in row.items() if k} for row in reader]
80
82
  sorted_rows = sorted(
81
- all_rows, key=lambda row: (row.get("site_name", ""), int(row.get("sid", 0)))
83
+ all_rows,
84
+ key=lambda row: (row.get("site_name", ""), int(row.get("sid", 0))),
82
85
  )
83
-
84
86
  # compare sorted CSV length with DB
85
87
  if len(sorted_rows) != self.randomizer_model_cls.objects.all().count():
86
88
  expected_cnt = len(sorted_rows)
@@ -95,9 +95,11 @@ class SiteVisitSchedules:
95
95
  def get_by_consent_definition(
96
96
  self,
97
97
  cdef: ConsentDefinition,
98
- ) -> tuple[VisitSchedule, Schedule]:
99
- """Returns a visit schedule instance or raises."""
100
- ret = []
98
+ ) -> tuple[tuple[VisitSchedule, Schedule], ...]:
99
+ """Returns a tuple of (visit schedule, schedule instances) that
100
+ match this cdef or raises.
101
+ """
102
+ visit_schedules = []
101
103
  attr = "consent_definitions"
102
104
  for visit_schedule in self.visit_schedules.values():
103
105
  for schedule in visit_schedule.schedules.values():
@@ -109,18 +111,12 @@ class SiteVisitSchedules:
109
111
  ) from e
110
112
  for _cdef in consent_definitions:
111
113
  if _cdef == cdef:
112
- ret.append([visit_schedule, schedule]) # noqa: PERF401
113
- if not ret:
114
+ visit_schedules.append((visit_schedule, schedule)) # noqa: PERF401
115
+ if not visit_schedules:
114
116
  raise SiteVisitScheduleError(
115
117
  f"Schedule not found. No schedule exists for {attr}={cdef}."
116
118
  )
117
- if len(ret) > 1:
118
- raise SiteVisitScheduleError(
119
- f"Schedule is ambiguous. More than one schedule exists for "
120
- f"{attr}={cdef}. Got {ret}"
121
- )
122
- visit_schedule, schedule = ret[0]
123
- return visit_schedule, schedule
119
+ return tuple(visit_schedules)
124
120
 
125
121
  def get_by_onschedule_model(self, onschedule_model: str) -> tuple[VisitSchedule, Schedule]:
126
122
  """Returns a tuple of (visit_schedule, schedule)