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

Files changed (64) hide show
  1. {clinicedc-2.0.20.dist-info → clinicedc-2.0.22.dist-info}/METADATA +1 -1
  2. {clinicedc-2.0.20.dist-info → clinicedc-2.0.22.dist-info}/RECORD +64 -64
  3. edc_appointment/utils.py +4 -5
  4. edc_pdf_reports/report.py +2 -2
  5. edc_reportable/reference_range_evaluator.py +1 -2
  6. edc_subject_dashboard/middleware.py +2 -3
  7. edc_subject_dashboard/requisition_labels.py +2 -2
  8. edc_subject_dashboard/requisition_report.py +10 -6
  9. edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +33 -33
  10. edc_subject_dashboard/view_mixins/subject_visit_view_mixin.py +1 -1
  11. edc_subject_dashboard/view_utils/crf_button.py +5 -4
  12. edc_subject_dashboard/view_utils/go_to_forms_button.py +1 -2
  13. edc_subject_dashboard/view_utils/subject_screening_button.py +2 -6
  14. edc_subject_dashboard/views/base_requisition_view.py +2 -3
  15. edc_subject_dashboard/views/refresh_appointments_view.py +6 -6
  16. edc_subject_dashboard/views/requisition_print_actions_view.py +5 -5
  17. edc_subject_dashboard/views/requisition_verify_actions_view.py +1 -1
  18. edc_timepoint/form_mixin.py +3 -3
  19. edc_timepoint/timepoint.py +1 -1
  20. edc_timepoint/timepoint_collection.py +3 -3
  21. edc_transfer/action_items.py +1 -2
  22. edc_transfer/form_validators.py +2 -2
  23. edc_unblinding/action_items.py +2 -4
  24. edc_unblinding/admin/autocomplete_admin.py +2 -2
  25. edc_unblinding/admin/unblinding_request_admin.py +2 -2
  26. edc_unblinding/admin/unblinding_review_admin.py +2 -2
  27. edc_unblinding/auths.py +11 -5
  28. edc_unblinding/models/unblinding_review.py +2 -2
  29. edc_utils/age.py +7 -3
  30. edc_utils/celery.py +3 -3
  31. edc_utils/context_processors_check.py +1 -1
  32. edc_utils/get_datetime_from_env.py +1 -1
  33. edc_utils/get_static_file.py +24 -14
  34. edc_utils/logging_filters/ignore_specific_ip_disallowed_host.py +1 -1
  35. edc_utils/message_in_queue.py +1 -4
  36. edc_utils/show_urls.py +1 -1
  37. edc_utils/text.py +1 -1
  38. edc_view_utils/model_button.py +2 -3
  39. edc_visit_schedule/exceptions.py +3 -3
  40. edc_visit_schedule/management/commands/find_invalid_onschedules.py +6 -4
  41. edc_visit_schedule/modelform_mixins/crf/visit_schedule_crf_modelform_mixin.py +1 -1
  42. edc_visit_schedule/modelform_mixins/off_schedule_modelform_mixin.py +3 -3
  43. edc_visit_schedule/modelform_mixins/visit_schedule_non_crf_modelform_mixin.py +1 -1
  44. edc_visit_schedule/models/signals.py +3 -8
  45. edc_visit_schedule/ordered_collection.py +4 -8
  46. edc_visit_schedule/post_migrate_signals.py +2 -2
  47. edc_visit_schedule/schedule/schedule.py +3 -7
  48. edc_visit_schedule/schedule/window.py +1 -1
  49. edc_visit_schedule/simple_model_validator.py +4 -4
  50. edc_visit_schedule/site_visit_schedules.py +12 -16
  51. edc_visit_schedule/subject_schedule.py +8 -8
  52. edc_visit_schedule/system_checks.py +3 -3
  53. edc_visit_schedule/templatetags/edc_visit_schedule_extras.py +2 -2
  54. edc_visit_schedule/utils.py +13 -14
  55. edc_visit_schedule/view_mixins.py +1 -1
  56. edc_visit_schedule/visit/crf.py +7 -6
  57. edc_visit_schedule/visit/forms_collection.py +2 -2
  58. edc_visit_schedule/visit/requisition.py +3 -3
  59. edc_visit_schedule/visit/visit.py +30 -30
  60. edc_visit_schedule/visit/window_period.py +3 -2
  61. edc_visit_schedule/visit_schedule/schedules_collection.py +5 -3
  62. edc_visit_schedule/visit_schedule/visit_schedule.py +5 -5
  63. {clinicedc-2.0.20.dist-info → clinicedc-2.0.22.dist-info}/WHEEL +0 -0
  64. {clinicedc-2.0.20.dist-info → clinicedc-2.0.22.dist-info}/licenses/LICENSE +0 -0
@@ -204,7 +204,7 @@ class Schedule:
204
204
  f"See {visit}. Got visit.timepoint={visit.timepoint}."
205
205
  )
206
206
  visit.base_timepoint = self.base_timepoint
207
- self.visits.update({visit.code: visit})
207
+ self.visits.update(**{visit.code: visit})
208
208
  return visit
209
209
 
210
210
  @property
@@ -235,9 +235,7 @@ class Schedule:
235
235
  visit_codes.append(visit_code)
236
236
  return visit_codes
237
237
 
238
- def subject(
239
- self, subject_identifier: str, consent_definition: ConsentDefinition = None
240
- ) -> SubjectSchedule:
238
+ def subject(self, subject_identifier: str) -> SubjectSchedule:
241
239
  """Returns a SubjectSchedule instance for this subject.
242
240
 
243
241
  Note: SubjectSchedule puts a subject on/off schedule by
@@ -292,9 +290,7 @@ class Schedule:
292
290
  f"'{consent_definition.proxy_model}', "
293
291
  f"version='{consent_definition.version}'). "
294
292
  )
295
- self.subject(
296
- subject_identifier, consent_definition=consent_definition
297
- ).put_on_schedule(
293
+ self.subject(subject_identifier).put_on_schedule(
298
294
  onschedule_datetime,
299
295
  skip_baseline=skip_baseline,
300
296
  skip_get_current_site=skip_get_current_site,
@@ -49,7 +49,7 @@ class Window:
49
49
  try:
50
50
  self.visits.get(self.visit_code)
51
51
  except VisitCollectionError as e:
52
- raise ScheduleError(e)
52
+ raise ScheduleError(e) from e
53
53
  if self.is_scheduled_visit or not self.visits.next(self.visit_code):
54
54
  self.raise_for_scheduled_not_in_window()
55
55
  else:
@@ -1,7 +1,7 @@
1
1
  from django.apps import apps as django_apps
2
2
 
3
3
 
4
- class InvalidModel(Exception):
4
+ class InvalidModel(Exception): # noqa: N818
5
5
  pass
6
6
 
7
7
 
@@ -14,11 +14,11 @@ class SimpleModelValidator:
14
14
  def __init__(self, model=None, attr=None):
15
15
  try:
16
16
  app_label, _ = model.split(".")
17
- except AttributeError:
18
- raise InvalidModel(f"Invalid label lower format for '{attr}'. " f"Got {model}")
17
+ except AttributeError as e:
18
+ raise InvalidModel(f"Invalid label lower format for '{attr}'. Got {model}") from e
19
19
  else:
20
20
  app_labels = [app_config.name for app_config in django_apps.get_app_configs()]
21
21
  if app_label not in app_labels:
22
22
  raise InvalidModel(
23
- f"Invalid model. app_label does not exist for " f"'{attr}'. Got {model}"
23
+ f"Invalid model. app_label does not exist for '{attr}'. Got {model}"
24
24
  )
@@ -121,9 +121,7 @@ class SiteVisitSchedules:
121
121
  visit_schedule, schedule = ret[0]
122
122
  return visit_schedule, schedule
123
123
 
124
- def get_by_onschedule_model(
125
- self, onschedule_model: str = None
126
- ) -> tuple[VisitSchedule, Schedule]:
124
+ def get_by_onschedule_model(self, onschedule_model: str) -> tuple[VisitSchedule, Schedule]:
127
125
  """Returns a tuple of (visit_schedule, schedule)
128
126
  for the given onschedule model.
129
127
 
@@ -132,7 +130,7 @@ class SiteVisitSchedules:
132
130
  return self.get_by_model(attr="onschedule_model", model=onschedule_model)
133
131
 
134
132
  def get_by_offschedule_model(
135
- self, offschedule_model: str = None
133
+ self, offschedule_model: str
136
134
  ) -> tuple[VisitSchedule, Schedule]:
137
135
  """Returns a tuple of visit_schedule, schedule
138
136
  for the given offschedule model.
@@ -142,7 +140,7 @@ class SiteVisitSchedules:
142
140
  return self.get_by_model(attr="offschedule_model", model=offschedule_model)
143
141
 
144
142
  def get_by_loss_to_followup_model(
145
- self, loss_to_followup_model: str = None
143
+ self, loss_to_followup_model: str
146
144
  ) -> tuple[VisitSchedule, Schedule]:
147
145
  """Returns a tuple of visit_schedule, schedule
148
146
  for the given loss_to_followup model.
@@ -151,9 +149,7 @@ class SiteVisitSchedules:
151
149
  """
152
150
  return self.get_by_model(attr="loss_to_followup_model", model=loss_to_followup_model)
153
151
 
154
- def get_by_model(
155
- self, attr: str = None, model: str = None
156
- ) -> tuple[VisitSchedule, Schedule]:
152
+ def get_by_model(self, attr: str, model: str) -> tuple[VisitSchedule, Schedule]:
157
153
  ret = []
158
154
  model = model.lower()
159
155
  for visit_schedule in self.visit_schedules.values():
@@ -178,14 +174,15 @@ class SiteVisitSchedules:
178
174
  visit_schedule, schedule = ret[0]
179
175
  return visit_schedule, schedule
180
176
 
181
- def get_by_offstudy_model(self, offstudy_model: str = None) -> list[VisitSchedule]:
177
+ def get_by_offstudy_model(self, offstudy_model: str) -> list[VisitSchedule]:
182
178
  """Returns a list of visit_schedules for the given
183
179
  offstudy model.
184
180
  """
185
- visit_schedules = []
186
- for visit_schedule in self.visit_schedules.values():
187
- if visit_schedule.offstudy_model == offstudy_model:
188
- visit_schedules.append(visit_schedule)
181
+ visit_schedules = [
182
+ visit_schedule
183
+ for visit_schedule in self.visit_schedules.values()
184
+ if visit_schedule.offstudy_model == offstudy_model
185
+ ]
189
186
  if not visit_schedules:
190
187
  raise SiteVisitScheduleError(
191
188
  f"No visit schedules have been defined using the "
@@ -220,7 +217,7 @@ class SiteVisitSchedules:
220
217
  @staticmethod
221
218
  def get_offstudy_model() -> str:
222
219
  offstudy_models = []
223
- for _, visit_schedule in site_visit_schedules.get_visit_schedules().items():
220
+ for visit_schedule in site_visit_schedules.get_visit_schedules().values():
224
221
  if visit_schedule.offstudy_model not in offstudy_models:
225
222
  offstudy_models.append(visit_schedule.offstudy_model)
226
223
  if len(offstudy_models) > 1:
@@ -236,8 +233,7 @@ class SiteVisitSchedules:
236
233
  "No off study model defined in visit_schedule. "
237
234
  f"Got registered visit_schedules: {visit_schedule_names}."
238
235
  )
239
- offstudy_model = offstudy_models[0]
240
- return offstudy_model
236
+ return offstudy_models[0]
241
237
 
242
238
  @property
243
239
  def all_post_consent_models(self) -> dict[str, str]:
@@ -237,13 +237,13 @@ class SubjectSchedule:
237
237
  schedule_name=self.schedule_name,
238
238
  visit_schedule_name=self.visit_schedule_name,
239
239
  )
240
- except ObjectDoesNotExist:
240
+ except ObjectDoesNotExist as e:
241
241
  raise NotOnScheduleError(
242
242
  "Failed to take subject off schedule. "
243
243
  f"Subject has not been put on schedule "
244
244
  f"'{self.visit_schedule_name}.{self.schedule_name}'. "
245
245
  f"Got '{self.subject_identifier}'."
246
- )
246
+ ) from e
247
247
 
248
248
  if history_obj:
249
249
  self.update_history_or_raise(
@@ -293,7 +293,7 @@ class SubjectSchedule:
293
293
  subject_identifier=self.subject_identifier,
294
294
  schedule_name=self.schedule_name,
295
295
  visit_schedule_name=self.visit_schedule_name,
296
- **{f"{related_visit_model_attr}__report_datetime__gt": (offschedule_datetime)},
296
+ **{f"{related_visit_model_attr}__report_datetime__gt": offschedule_datetime},
297
297
  )
298
298
  except ObjectDoesNotExist:
299
299
  appointments = None
@@ -302,7 +302,7 @@ class SubjectSchedule:
302
302
  subject_identifier=self.subject_identifier,
303
303
  schedule_name=self.schedule_name,
304
304
  visit_schedule_name=self.visit_schedule_name,
305
- **{f"{related_visit_model_attr}__report_datetime__gt": (offschedule_datetime)},
305
+ **{f"{related_visit_model_attr}__report_datetime__gt": offschedule_datetime},
306
306
  )
307
307
  if appointments:
308
308
  raise InvalidOffscheduleDate(
@@ -336,12 +336,12 @@ class SubjectSchedule:
336
336
  model_cls = django_apps.get_model(self.registered_subject_model)
337
337
  try:
338
338
  obj = model_cls.objects.get(subject_identifier=self.subject_identifier)
339
- except ObjectDoesNotExist:
339
+ except ObjectDoesNotExist as e:
340
340
  raise UnknownSubjectError(
341
341
  f"Failed to put subject on schedule. Unknown subject. "
342
342
  f"Searched `{self.registered_subject_model}`. "
343
343
  f"Got subject_identifier=`{self.subject_identifier}`."
344
- )
344
+ ) from e
345
345
  return obj
346
346
 
347
347
  @property
@@ -350,11 +350,11 @@ class SubjectSchedule:
350
350
  onschedule_obj = self.onschedule_model_cls.objects.get(
351
351
  subject_identifier=self.subject_identifier
352
352
  )
353
- except ObjectDoesNotExist:
353
+ except ObjectDoesNotExist as e:
354
354
  raise NotOnScheduleError(
355
355
  f"Subject has not been put on a schedule `{self.schedule_name}`. "
356
356
  f"Got subject_identifier=`{self.subject_identifier}`."
357
- )
357
+ ) from e
358
358
  return onschedule_obj
359
359
 
360
360
  def onschedule_or_raise(self, report_datetime=None, compare_as_datetimes=None):
@@ -5,7 +5,7 @@ from collections import Counter, defaultdict
5
5
  from typing import TYPE_CHECKING
6
6
 
7
7
  from django.apps import apps as django_apps
8
- from django.core.checks import Error, Warning
8
+ from django.core.checks import Error, Warning # noqa: A004
9
9
  from django.core.exceptions import ObjectDoesNotExist
10
10
 
11
11
  from .site_visit_schedules import site_visit_schedules
@@ -55,7 +55,7 @@ def check_subject_schedule_history(app_configs, **kwargs) -> list:
55
55
  )
56
56
  for obj in subject_schedule_history_cls.objects.all():
57
57
  try:
58
- obj.onschedule_obj
58
+ obj.onschedule_obj # noqa: B018
59
59
  except LookupError as e:
60
60
  errors.append(
61
61
  Error(
@@ -201,7 +201,7 @@ def check_multiple_proxies_same_proxy_root(
201
201
  if proxies_counter & proxies_sharing_roots_counter == proxies_counter:
202
202
  # OK if proxies counter reflects ALL defined proxy shared roots
203
203
  del proxy_root_to_child_proxies[proxy_root]
204
- elif len(proxies_counter) == 1 and next(iter(proxies_counter.values())) <= 2:
204
+ elif len(proxies_counter) == 1 and next(iter(proxies_counter.values())) <= 2: # noqa: PLR2004
205
205
  # OK for a single proxy to be defined in two places (CRFs collection + PRNs)
206
206
  del proxy_root_to_child_proxies[proxy_root]
207
207
  else:
@@ -51,7 +51,7 @@ def subject_schedule_footer_row(
51
51
  context.update(
52
52
  offschedule_datetime=None,
53
53
  onschedule_datetime=onschedule_model_obj.onschedule_datetime,
54
- href=mark_safe(href), # nosec B703, B308
54
+ href=mark_safe(href), # nosec B703, B308 # noqa: S308
55
55
  )
56
56
  elif history_obj.offschedule_datetime:
57
57
  # subject is OFF this schedule (offschedule_model_obj)
@@ -71,6 +71,6 @@ def subject_schedule_footer_row(
71
71
  context.update(
72
72
  offschedule_datetime=history_obj.offschedule_datetime,
73
73
  onschedule_datetime=onschedule_model_obj.onschedule_datetime,
74
- href=mark_safe(href), # nosec B703, B308
74
+ href=mark_safe(href), # nosec B703, B308 # noqa: S308
75
75
  )
76
76
  return context
@@ -76,9 +76,7 @@ def raise_if_not_baseline(subject_visit) -> None:
76
76
  raise forms.ValidationError("This form is only available for completion at baseline.")
77
77
 
78
78
 
79
- def get_onschedule_models(
80
- subject_identifier: str = None, report_datetime: datetime = None
81
- ) -> list[str]:
79
+ def get_onschedule_models(subject_identifier: str, report_datetime: datetime) -> list[str]:
82
80
  """Returns a list of onschedule models, in label_lower format,
83
81
  for this subject and date.
84
82
  """
@@ -96,7 +94,7 @@ def get_onschedule_models(
96
94
  return onschedule_models
97
95
 
98
96
 
99
- def get_offschedule_models(subject_identifier=None, report_datetime=None) -> list[str]:
97
+ def get_offschedule_models(subject_identifier: str, report_datetime: datetime) -> list[str]:
100
98
  """Returns a list of offschedule models, in label_lower format,
101
99
  for this subject and date.
102
100
 
@@ -141,7 +139,7 @@ def off_schedule_or_raise(
141
139
  )
142
140
 
143
141
 
144
- def off_all_schedules_or_raise(subject_identifier: str = None):
142
+ def off_all_schedules_or_raise(subject_identifier: str):
145
143
  """Raises an exception if subject is still on any schedule."""
146
144
  for visit_schedule in site_visit_schedules.get_visit_schedules().values():
147
145
  for schedule in visit_schedule.schedules.values():
@@ -171,8 +169,8 @@ def off_all_schedules_or_raise(subject_identifier: str = None):
171
169
 
172
170
 
173
171
  def offstudy_datetime_after_all_offschedule_datetimes(
174
- subject_identifier: str = None,
175
- offstudy_datetime: datetime = None,
172
+ subject_identifier: str,
173
+ offstudy_datetime: datetime,
176
174
  exception_cls=None,
177
175
  ) -> None:
178
176
  exception_cls = exception_cls or forms.ValidationError
@@ -206,10 +204,10 @@ def offstudy_datetime_after_all_offschedule_datetimes(
206
204
 
207
205
 
208
206
  def report_datetime_within_onschedule_offschedule_datetimes(
209
- subject_identifier: str = None,
210
- report_datetime: datetime = None,
211
- visit_schedule_name: str = None,
212
- schedule_name: str = None,
207
+ subject_identifier: str,
208
+ report_datetime: datetime,
209
+ visit_schedule_name: str,
210
+ schedule_name: str,
213
211
  exception_cls=None,
214
212
  ):
215
213
  exception_cls = exception_cls or forms.ValidationError
@@ -219,11 +217,11 @@ def report_datetime_within_onschedule_offschedule_datetimes(
219
217
  onschedule_obj = schedule.onschedule_model_cls.objects.get(
220
218
  subject_identifier=subject_identifier
221
219
  )
222
- except ObjectDoesNotExist:
220
+ except ObjectDoesNotExist as e:
223
221
  raise OnScheduleError(
224
222
  f"Subject is not on schedule. {visit_schedule_name}.{schedule_name}. "
225
223
  f"Got {subject_identifier}"
226
- )
224
+ ) from e
227
225
  try:
228
226
  offschedule_obj = schedule.offschedule_model_cls.objects.get(
229
227
  subject_identifier=subject_identifier,
@@ -280,7 +278,7 @@ def get_onschedule_model_instance(
280
278
  raise OffScheduleError(
281
279
  "Subject is not on a schedule. Using subject_identifier="
282
280
  f"`{subject_identifier}` and appt_datetime=`{dte_as_str}`. Got {e}"
283
- )
281
+ ) from e
284
282
  return onschedule_obj
285
283
 
286
284
 
@@ -302,6 +300,7 @@ def get_proxy_root_model(proxy_model: models.Model) -> models.Model | None:
302
300
  """
303
301
  if proxy_model._meta.proxy:
304
302
  return proxy_model._meta.concrete_model
303
+ return None
305
304
 
306
305
 
307
306
  def check_models_in_visit_schedule() -> dict[str, list]:
@@ -37,7 +37,7 @@ class VisitScheduleViewMixin:
37
37
  pass
38
38
  else:
39
39
  self.onschedule_models.append(onschedule_model_obj)
40
- self.visit_schedules.update({visit_schedule.name: visit_schedule})
40
+ self.visit_schedules.update(**{visit_schedule.name: visit_schedule})
41
41
  if schedule.is_onschedule(self.subject_identifier, timezone.now()):
42
42
  self.current_schedule = schedule
43
43
  self.current_visit_schedule = visit_schedule
@@ -15,12 +15,13 @@ class CrfModelNotProxyModelError(Exception):
15
15
  class Crf:
16
16
  def __init__(
17
17
  self,
18
- show_order: int = None,
19
- model: str = None,
20
- required: bool = None,
21
- additional: bool = None,
22
- site_ids: list[int] = None,
23
- shares_proxy_root: bool = None,
18
+ *,
19
+ show_order: int,
20
+ model: str,
21
+ required: bool | None = None,
22
+ additional: bool | None = None,
23
+ site_ids: list[int] | None = None,
24
+ shares_proxy_root: bool | None = None,
24
25
  ) -> None:
25
26
  self.additional = additional
26
27
  self.model = model.lower()
@@ -19,7 +19,7 @@ class FormsCollection:
19
19
  *forms: Crf | Requisition,
20
20
  name: str | None = None,
21
21
  check_sequence: bool | None = None,
22
- **kwargs,
22
+ **kwargs, # noqa: ARG002
23
23
  ):
24
24
  check_sequence = True if check_sequence is None else check_sequence
25
25
  self.collection_is_unique_or_raise(forms)
@@ -85,7 +85,7 @@ class FormsCollection:
85
85
  def insert(self, index, value):
86
86
  if value:
87
87
  forms = list(self._forms)
88
- for index, item in forms:
88
+ for _, item in forms:
89
89
  if item.name == value.name:
90
90
  raise FormsCollectionError(
91
91
  f"Insert failed. Item is not unique. Got {value.name}"
@@ -14,7 +14,7 @@ class ScheduledRequisitionError(Exception):
14
14
 
15
15
 
16
16
  class Requisition(Crf):
17
- def __init__(self, panel=None, required: bool = None, **kwargs):
17
+ def __init__(self, panel=None, required: bool | None = None, **kwargs):
18
18
  required = False if required is None else required
19
19
  self.panel = panel
20
20
  if not self.panel.requisition_model:
@@ -52,10 +52,10 @@ class Requisition(Crf):
52
52
 
53
53
  See also: edc_lab.
54
54
  """
55
- from edc_lab.site_labs import site_labs
55
+ from edc_lab.site_labs import site_labs # noqa: PLC0415
56
56
 
57
57
  try:
58
- self.panel.requisition_model_cls
58
+ self.panel.requisition_model_cls # noqa: B018
59
59
  except LookupError as e:
60
60
  raise RequisitionLookupError(e) from e
61
61
 
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
20
20
 
21
21
  from dateutil.relativedelta import relativedelta
22
22
 
23
- from edc_facility import Facility
23
+ from edc_facility.facility import Facility
24
24
 
25
25
  from .crf import Crf
26
26
  from .requisition import Requisition
@@ -38,7 +38,7 @@ class VisitError(Exception):
38
38
  pass
39
39
 
40
40
 
41
- class BaseDatetimeNotSet(Exception):
41
+ class BaseDatetimeNotSet(Exception): # noqa: N818
42
42
  pass
43
43
 
44
44
 
@@ -53,10 +53,11 @@ class VisitDate:
53
53
 
54
54
  def __init__(
55
55
  self,
56
- rlower: relativedelta = None,
57
- rupper: relativedelta = None,
58
- timepoint: Decimal = None,
59
- base_timepoint: Decimal = None,
56
+ *,
57
+ rlower: relativedelta,
58
+ rupper: relativedelta,
59
+ timepoint: Decimal | None = None,
60
+ base_timepoint: Decimal | None = None,
60
61
  ):
61
62
  self._base: datetime | None = None
62
63
  self._lower: datetime | None = None
@@ -73,7 +74,7 @@ class VisitDate:
73
74
  return self._base
74
75
 
75
76
  @base.setter
76
- def base(self, dt: datetime = None):
77
+ def base(self, dt: datetime):
77
78
  self._base = to_local(dt)
78
79
  self._lower, self._upper = self._window_period.get_window(dt=self._base)
79
80
 
@@ -100,25 +101,25 @@ class Visit:
100
101
 
101
102
  def __init__(
102
103
  self,
103
- code: str = None,
104
- timepoint: int | float | Decimal = None,
105
- rbase: relativedelta = None,
106
- rlower: relativedelta = None,
107
- rupper: relativedelta = None,
108
- rlower_late: relativedelta = None,
109
- rupper_late: relativedelta = None,
110
- add_window_gap_to_lower: bool | None = None,
111
- max_window_gap_to_lower: int | None = None,
104
+ code: str,
105
+ timepoint: int | float | Decimal,
106
+ rbase: relativedelta,
107
+ rlower: relativedelta,
108
+ rupper: relativedelta,
109
+ title: str | None = None,
110
+ facility_name: str | None = None,
112
111
  crfs: CrfCollection | None = None,
113
- requisitions: RequisitionCollection | None = None,
112
+ crfs_prn: CrfCollection | None = None,
114
113
  crfs_unscheduled: CrfCollection | None = None,
115
114
  crfs_missed: CrfCollection | None = None,
116
- requisitions_unscheduled: RequisitionCollection | None = None,
117
- crfs_prn: CrfCollection | None = None,
115
+ requisitions: RequisitionCollection | None = None,
118
116
  requisitions_prn: RequisitionCollection | None = None,
119
- title: str = None,
117
+ requisitions_unscheduled: RequisitionCollection | None = None,
118
+ rlower_late: relativedelta = None,
119
+ rupper_late: relativedelta = None,
120
+ add_window_gap_to_lower: bool | None = None,
121
+ max_window_gap_to_lower: int | None = None,
120
122
  allow_unscheduled: bool | None = None,
121
- facility_name: str | None = None,
122
123
  instructions: str | None = None,
123
124
  base_timepoint: int | float | Decimal | None = None,
124
125
  grouping=None,
@@ -162,14 +163,14 @@ class Visit:
162
163
  raise VisitCodeError(f"Invalid visit code. Got '{code}'")
163
164
  self.code = code # unique
164
165
  self.dates = self.visit_date_cls(
165
- rlower=rlower,
166
- rupper=rupper,
166
+ rlower=self.rlower,
167
+ rupper=self.rupper,
167
168
  timepoint=self.timepoint,
168
169
  base_timepoint=self.base_timepoint,
169
170
  )
170
171
  self.late_dates = self.visit_date_cls(
171
- rlower=rlower_late or rlower,
172
- rupper=rupper_late or rupper,
172
+ rlower=self.rlower_late,
173
+ rupper=self.rupper_late,
173
174
  timepoint=self.timepoint,
174
175
  base_timepoint=self.base_timepoint,
175
176
  )
@@ -252,11 +253,10 @@ class Visit:
252
253
  return get_requisition
253
254
 
254
255
  def get_models(self) -> list:
255
- models = []
256
- for crf in self.crfs:
257
- models.append(django_apps.get_model(crf.model))
258
- for crf in self.requisitions:
259
- models.append(django_apps.get_model(crf.model))
256
+ models = [django_apps.get_model(crf.model) for crf in self.crfs]
257
+ models.extend(
258
+ [django_apps.get_model(requisition.model) for requisition in self.requisitions]
259
+ )
260
260
  return models
261
261
 
262
262
  @property
@@ -14,8 +14,9 @@ from edc_utils import to_local
14
14
  class WindowPeriod:
15
15
  def __init__(
16
16
  self,
17
- rlower: relativedelta = None,
18
- rupper: relativedelta = None,
17
+ *,
18
+ rlower: relativedelta,
19
+ rupper: relativedelta,
19
20
  timepoint: Decimal | None = None,
20
21
  base_timepoint: Decimal | None = None,
21
22
  no_floor: bool | None = None,
@@ -10,11 +10,13 @@ class SchedulesCollection(OrderedCollection):
10
10
  key = "name"
11
11
  ordering_attr = "sequence"
12
12
 
13
- def __init__(self, visit_schedule_name: str = None, *args, **kwargs) -> None:
13
+ def __init__(self, visit_schedule_name: str, *args, **kwargs) -> None:
14
14
  self.visit_schedule_name = visit_schedule_name
15
15
  super().__init__(*args, **kwargs)
16
16
 
17
- def get_schedule(self, model: str = None, schedule_name: str = None) -> Schedule:
17
+ def get_schedule(
18
+ self, model: str | None = None, schedule_name: str | None = None
19
+ ) -> Schedule:
18
20
  """Returns a schedule or raises; by name, by onschedule/offschedule model
19
21
  or by model label_lower.
20
22
  """
@@ -22,7 +24,7 @@ class SchedulesCollection(OrderedCollection):
22
24
  if model:
23
25
  model = model.lower()
24
26
  for item in self.values():
25
- if item.onschedule_model == model or item.offschedule_model == model:
27
+ if model in (item.onschedule_model, item.offschedule_model):
26
28
  schedule = item
27
29
  if schedule:
28
30
  break
@@ -27,7 +27,7 @@ class VisitScheduleAppointmentModelError(Exception):
27
27
  pass
28
28
 
29
29
 
30
- class AlreadyRegisteredSchedule(Exception):
30
+ class AlreadyRegisteredSchedule(Exception): # noqa: N818
31
31
  pass
32
32
 
33
33
 
@@ -35,8 +35,8 @@ class VisitSchedule:
35
35
  name_regex = r"[a-z0-9\_\-]+$"
36
36
  name_regex_msg = "numbers, lower case letters and '_'"
37
37
  schedules_collection = SchedulesCollection
38
- create_metadata_on_reasons = [SCHEDULED, UNSCHEDULED, MISSED_VISIT]
39
- delete_metadata_on_reasons = []
38
+ create_metadata_on_reasons: tuple[str, ...] = (SCHEDULED, UNSCHEDULED, MISSED_VISIT)
39
+ delete_metadata_on_reasons: tuple[str, ...] = ()
40
40
 
41
41
  def __init__(
42
42
  self,
@@ -77,7 +77,7 @@ class VisitSchedule:
77
77
 
78
78
  if not re.match(self.name_regex, name):
79
79
  raise VisitScheduleNameError(
80
- f"Visit schedule name may only contain {self.name_regex_msg}. " f"Got {name}"
80
+ f"Visit schedule name may only contain {self.name_regex_msg}. Got {name}"
81
81
  )
82
82
  self.title = self.verbose_name = verbose_name or " ".join(
83
83
  [s.capitalize() for s in name.split("_")]
@@ -107,7 +107,7 @@ class VisitSchedule:
107
107
  raise AlreadyRegisteredSchedule(
108
108
  f"Schedule '{schedule.name}' is already registered. See '{self}'"
109
109
  )
110
- self.schedules.update({schedule.name: schedule})
110
+ self.schedules.update(**{schedule.name: schedule})
111
111
  self._all_post_consent_models = None
112
112
  return schedule
113
113