Skip to content

Commit

Permalink
reinstantiate SubjectSchedule each time it is accessed in schedule. f…
Browse files Browse the repository at this point in the history
…ix tests
  • Loading branch information
erikvw committed Jan 18, 2024
1 parent c663944 commit d53adfa
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 54 deletions.
15 changes: 12 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ Create a new visit schedule:
name='visit_schedule',
verbose_name='My Visit Schedule',
death_report_model=SubjectDeathReport,
offstudy_model=SubjectOffstudy,
visit_model=SubjectVisit)
offstudy_model=SubjectOffstudy)
Visit schedules contain ``Schedules`` so create a schedule:
Expand All @@ -79,7 +78,17 @@ Visit schedules contain ``Schedules`` so create a schedule:
schedule = Schedule(
name='schedule',
onschedule_model='myapp.onschedule',
offschedule_model='myapp.offschedule')
offschedule_model='myapp.offschedule',
consent_definitions=[consent_definition_v1])
About consent_definitions:
As you will see below, the ``schedule`` is a container for a data collection schedule of forms (CRFs and requisitions)
for a single study timepoint or ``visit``. Ethically, a subject's data may not be collected before the subject has signed and submitted the informed consent form (ICF).
``Schedule`` is configured with information about the ICF that covers the forms it contains. When a form for a subject is validated and submitted, the ``Schedule`` will
provide the consent_definition (or definitions) so that the calling object can confirm the subject is consented. The ICF is represented by
the class ``ConsentDefinition`` from ``edc_consent``.

See also class ``ConsentDefinition`` in ``edc_consent``.

Schedules contains visits, so declare some visits and add to the ``schedule``:

Expand Down
7 changes: 2 additions & 5 deletions edc_visit_schedule/model_mixins/on_schedule_model_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,9 @@ def save(self, *args, **kwargs):
self.report_datetime = self.onschedule_datetime
super().save(*args, **kwargs)

def put_on_schedule(self):
def put_on_schedule(self) -> None:
_, schedule = site_visit_schedules.get_by_onschedule_model(self._meta.label_lower)
schedule.put_on_schedule(
subject_identifier=self.subject_identifier,
onschedule_datetime=self.onschedule_datetime,
)
schedule.put_on_schedule(self.subject_identifier, self.onschedule_datetime)

@property
def visit_schedule(self) -> VisitSchedule:
Expand Down
68 changes: 33 additions & 35 deletions edc_visit_schedule/schedule/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@
from edc_sites.single_site import SingleSite
from edc_utils import formatted_date

from ..site_visit_schedules import SiteVisitScheduleError, site_visit_schedules
from ..subject_schedule import (
NotOnScheduleError,
NotOnScheduleForDateError,
SubjectSchedule,
SubjectScheduleError,
)
from ..exceptions import NotOnScheduleError, NotOnScheduleForDateError
from ..site_visit_schedules import site_visit_schedules
from ..subject_schedule import SubjectSchedule
from ..visit import Visit
from .visit_collection import VisitCollection
from .window import Window
Expand Down Expand Up @@ -81,7 +77,6 @@ def __init__(
sequence: str | None = None,
base_timepoint: float | Decimal | None = None,
):
self._subject = None
if not name or not re.match(r"[a-z0-9_\-]+$", name):
raise ScheduleNameError(
f"Invalid name. Got '{name}'. May only contains numbers, "
Expand Down Expand Up @@ -112,14 +107,6 @@ def __init__(
)
self.history_model = history_model or "edc_visit_schedule.subjectschedulehistory"

def check(self):
warnings = []
try:
self.subject.check()
except (SiteVisitScheduleError, SubjectScheduleError) as e:
warnings.append(f"{e} See schedule '{self.name}'.")
return warnings

def __repr__(self):
return f"Schedule({self.name})"

Expand Down Expand Up @@ -181,37 +168,48 @@ def requisition_required_at(self, requisition_panel) -> list:
return visit_codes

def subject(self, subject_identifier: str) -> SubjectSchedule:
"""Returns a SubjectSchedule instance.
"""Returns a SubjectSchedule instance for this subject.
Note: SubjectSchedule puts a subject on to a schedule or takes a subject
off of a schedule.
Note: SubjectSchedule puts a subject on/off schedule by
updating the on/offschedule models
"""
if not self._subject:
visit_schedule, schedule = site_visit_schedules.get_by_onschedule_model(
self.onschedule_model
)
if schedule.name != self.name:
raise ValueError(
f"Site visit schedules return the wrong schedule object. "
f"Expected {repr(self)} for onschedule_model={self.onschedule_model}. "
f"Got {repr(schedule)}."
)
self._subject = SubjectSchedule(
subject_identifier, visit_schedule=visit_schedule, schedule=self
visit_schedule, schedule = site_visit_schedules.get_by_onschedule_model(
self.onschedule_model
)
if schedule.name != self.name:
raise ValueError(
f"Site visit schedules return the wrong schedule object. "
f"Expected {repr(self)} for onschedule_model={self.onschedule_model}. "
f"Got {repr(schedule)}."
)
return self._subject
return SubjectSchedule(
subject_identifier, visit_schedule=visit_schedule, schedule=self
)

def put_on_schedule(self, subject_identifier: str, onschedule_datetime: datetime | None):
"""Wrapper method to puts a subject onto this schedule."""
self.subject(subject_identifier).put_on_schedule(onschedule_datetime)
def put_on_schedule(
self,
subject_identifier: str,
onschedule_datetime: datetime | None,
skip_baseline: bool | None = None,
):
"""Puts a subject onto this schedule.
Wrapper of method SubjectSchedule.put_on_schedule.
"""
self.subject(subject_identifier).put_on_schedule(
onschedule_datetime, skip_baseline=skip_baseline
)

def refresh_schedule(self, subject_identifier: str):
"""Resaves the onschedule model to, for example, refresh
appointments.
Wrapper of method SubjectSchedule.resave.
"""
self.subject(subject_identifier).resave()

def take_off_schedule(self, subject_identifier: str, offschedule_datetime: datetime):
"""Wrapper of method SubjectSchedule.take_off_schedule."""
self.subject(subject_identifier).take_off_schedule(offschedule_datetime)

def is_onschedule(self, subject_identifier: str, report_datetime: datetime):
Expand Down
18 changes: 9 additions & 9 deletions edc_visit_schedule/subject_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
NotOnScheduleError,
NotOnScheduleForDateError,
OnScheduleFirstAppointmentDateError,
SubjectScheduleError,
UnknownSubjectError,
)

Expand Down Expand Up @@ -62,6 +61,15 @@ def __init__(
self.offschedule_model: str = schedule.offschedule_model
self.appointment_model: str = schedule.appointment_model

def __repr__(self):
return (
f"{self.__class__.__name__}(subject_identifier={self.subject_identifier},"
f"visit_schedule={self.visit_schedule},schedule={self.schedule})"
)

def __str__(self):
return f"{self.subject_identifier} {self.visit_schedule_name}.{self.schedule_name}"

@property
def onschedule_model_cls(self) -> Type[OnSchedule]:
return django_apps.get_model(self.onschedule_model)
Expand Down Expand Up @@ -337,11 +345,3 @@ def onschedule_or_raise(self, report_datetime=None, compare_as_datetimes=None):
f"off this schedule on '{formatted_offschedule_datetime}'."
)
return None

def check(self):
try:
self.onschedule_model_cls
self.offschedule_model_cls
self.appointment_model_cls
except LookupError as e:
raise SubjectScheduleError(e)
3 changes: 1 addition & 2 deletions edc_visit_schedule/tests/tests/test_subject_schedule.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime

from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase, override_settings, tag
from django.test import TestCase, override_settings
from edc_consent.site_consents import site_consents
from edc_sites.tests import SiteTestCaseMixin
from edc_utils import get_utcnow
Expand Down Expand Up @@ -119,7 +119,6 @@ def test_multpile_consents(self):
except SubjectScheduleError:
self.fail("SubjectScheduleError unexpectedly raised.")

@tag("1")
def test_resave(self):
"""Asserts returns the correct instances for the schedule."""
visit_schedule, schedule = site_visit_schedules.get_by_onschedule_model(
Expand Down

0 comments on commit d53adfa

Please sign in to comment.