Skip to content

Commit

Permalink
reset visit_code_sequence in appointment_view_mixin for appts with un…
Browse files Browse the repository at this point in the history
…scheduled
  • Loading branch information
erikvw committed Jan 17, 2025
1 parent 1855843 commit 5fcbdeb
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 32 deletions.
146 changes: 139 additions & 7 deletions edc_appointment/tests/tests/test_appt_sequence2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.test import TestCase, override_settings, tag
from edc_consent import site_consents
from edc_facility.import_holidays import import_holidays
from edc_metadata.models import CrfMetadata
from edc_protocol.research_protocol_config import ResearchProtocolConfig
from edc_visit_schedule.site_visit_schedules import site_visit_schedules

Expand All @@ -20,6 +21,7 @@
from edc_appointment_app.visit_schedule import get_visit_schedule1

from ...creators import UnscheduledAppointmentCreator
from ...utils import reset_visit_code_sequence_or_pass
from ..helper import Helper

utc_tz = ZoneInfo("UTC")
Expand Down Expand Up @@ -75,13 +77,15 @@ def create_unscheduled(appointment: Appointment, days: int = None):
appointment.save_base(update_fields=["appt_status"])
return appointment

@staticmethod
def get_visit_codes(by: str = None, **kwargs):
return [
f"{o.visit_code}.{o.visit_code_sequence}"
for o in Appointment.objects.all().order_by(by)
]

@tag("4")
def test_resequence_appointment_on_insert_between_two_unscheduled(self):
def get_visit_codes(by: str = None):
return [
f"{o.visit_code}.{o.visit_code_sequence}"
for o in Appointment.objects.all().order_by(by)
]

appointment = Appointment.objects.get(visit_code="1000", visit_code_sequence=0)
self.assertEqual(self.create_unscheduled(appointment, days=2).visit_code_sequence, 1)
Expand All @@ -90,7 +94,7 @@ def get_visit_codes(by: str = None):

self.assertEqual(
["1000.0", "1000.1", "1000.2", "1000.3", "2000.0", "3000.0", "4000.0"],
get_visit_codes(by="appt_datetime"),
self.get_visit_codes(by="appt_datetime"),
)

# insert an appt between 1000.1 and 1000.2
Expand All @@ -108,5 +112,133 @@ def get_visit_codes(by: str = None):

self.assertEqual(
["1000.0", "1000.1", "1000.2", "1000.3", "1000.4", "2000.0", "3000.0", "4000.0"],
get_visit_codes(by="appt_datetime"),
self.get_visit_codes(by="appt_datetime"),
)

@tag("4")
def test_repair_visit_code_sequences(self):
appointment = Appointment.objects.get(visit_code="1000", visit_code_sequence=0)
self.create_unscheduled(appointment, days=2)
appt2 = self.create_unscheduled(appointment, days=4)
appt3 = self.create_unscheduled(appointment, days=5)

appt2.visit_code_sequence = 3333
appt2.save_base(update_fields=["visit_code_sequence"])

appt3.visit_code_sequence = 33
appt3.save_base(update_fields=["visit_code_sequence"])

self.assertEqual(
["1000.0", "1000.1", "1000.3333", "1000.33", "2000.0", "3000.0", "4000.0"],
self.get_visit_codes(by="appt_datetime"),
)

reset_visit_code_sequence_or_pass(
subject_identifier=self.subject_identifier,
visit_schedule_name=self.visit_schedule.name,
schedule_name="schedule1",
visit_code="1000",
)

self.assertEqual(
["1000.0", "1000.1", "1000.2", "1000.3", "2000.0", "3000.0", "4000.0"],
self.get_visit_codes(by="appt_datetime"),
)

@tag("4")
def test_repair_visit_code_sequences_with_related_visit(self):
appointment = Appointment.objects.get(visit_code="1000", visit_code_sequence=0)
appt1 = self.create_unscheduled(appointment, days=2)
self.create_related_visit(appt1)
appt2 = self.create_unscheduled(appointment, days=4)
self.create_related_visit(appt2)
appt3 = self.create_unscheduled(appointment, days=5)
self.create_related_visit(appt3)

appt2.visit_code_sequence = 3333
appt2.save_base(update_fields=["visit_code_sequence"])
appt2.related_visit.visit_code_sequence = 3333
appt2.related_visit.save_base(update_fields=["visit_code_sequence"])

appt3.visit_code_sequence = 33
appt3.save_base(update_fields=["visit_code_sequence"])
appt2.related_visit.visit_code_sequence = 33
appt2.related_visit.save_base(update_fields=["visit_code_sequence"])

self.assertEqual(
["1000.0", "1000.1", "1000.3333", "1000.33", "2000.0", "3000.0", "4000.0"],
self.get_visit_codes(by="appt_datetime"),
)

reset_visit_code_sequence_or_pass(
subject_identifier=self.subject_identifier,
visit_schedule_name=self.visit_schedule.name,
schedule_name="schedule1",
visit_code="1000",
)

self.assertEqual(
["1000.0", "1000.1", "1000.2", "1000.3", "2000.0", "3000.0", "4000.0"],
self.get_visit_codes(by="appt_datetime"),
)

self.assertEqual(
["1000.0", "1000.1", "1000.2", "1000.3", "2000.0"],
[
f"{o.visit_code}.{o.visit_code_sequence}"
for o in Appointment.objects.all().order_by("appt_datetime")
if getattr(o, "related_visit", None)
],
)

@tag("4")
def test_repair_visit_code_sequences_with_metadata(self):
appointment = Appointment.objects.get(visit_code="1000", visit_code_sequence=0)
appt1 = self.create_unscheduled(appointment, days=2)
self.create_related_visit(appt1)
appt2 = self.create_unscheduled(appointment, days=4)
self.create_related_visit(appt2)
appt3 = self.create_unscheduled(appointment, days=5)
self.create_related_visit(appt3)

appt2.visit_code_sequence = 3333
appt2.save_base(update_fields=["visit_code_sequence"])
appt2.related_visit.visit_code_sequence = 3333
appt2.related_visit.save_base(update_fields=["visit_code_sequence"])
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=2).update(
visit_code_sequence=3333
)

appt3.visit_code_sequence = 33
appt3.save_base(update_fields=["visit_code_sequence"])
appt2.related_visit.visit_code_sequence = 33
appt2.related_visit.save_base(update_fields=["visit_code_sequence"])
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=3).update(
visit_code_sequence=33
)

self.assertEqual(
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=3333).count(), 3
)
self.assertEqual(
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=33).count(), 3
)
reset_visit_code_sequence_or_pass(
subject_identifier=self.subject_identifier,
visit_schedule_name=self.visit_schedule.name,
schedule_name="schedule1",
visit_code="1000",
)
self.assertEqual(
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=3333).count(), 0
)
self.assertEqual(
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=33).count(), 0
)

self.assertEqual(
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=2).count(), 3
)
self.assertEqual(
CrfMetadata.objects.filter(visit_code="1000", visit_code_sequence=3).count(), 3
)
55 changes: 31 additions & 24 deletions edc_appointment/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.core.handlers.wsgi import WSGIRequest
from django.db import transaction
from django.db.models import ProtectedError
from django.db.models import Count, ProtectedError
from django.urls import reverse
from django.utils.translation import gettext as _
from edc_constants.constants import CLINIC
Expand Down Expand Up @@ -256,10 +256,13 @@ def reset_visit_code_sequence_or_pass(
visit_code: str = None,
appointment: Appointment | None = None,
) -> Appointment | None:
"""Check visit code sequence order relative to appt_datetime
and reset if needed.
"""Validate the order of the appointment visit code sequences
relative to the appt_datetime and reset the visit code sequences
if needed.
Also update related_visit if it exists.
Delete and recreate metadata
Also do the same for the `related_visit`, if it exists.
"""
opts = dict(
subject_identifier=subject_identifier,
Expand Down Expand Up @@ -306,26 +309,30 @@ def reset_visit_code_sequence_or_pass(
return appointment


# def reset_visit_code_sequence_for_subject(
# subject_identifier: str = None,
# visit_schedule_name: str = None,
# schedule_name: str = None,
# ) -> None:
# for obj in (
# get_appointment_model_cls()
# .objects.filter(
# subject_identifier=subject_identifier,
# visit_schedule_name=visit_schedule_name,
# schedule_name=schedule_name,
# )
# .order_by("appt_datetime")
# ):
# reset_visit_code_sequence_or_pass(
# subject_identifier=subject_identifier,
# visit_schedule_name=visit_schedule_name,
# schedule_name=schedule_name,
# visit_code=obj.visit_code,
# )
def reset_visit_code_sequence_for_subject(
subject_identifier: str = None,
visit_schedule_name: str = None,
schedule_name: str = None,
) -> None:
"""Resets / validates appointment `visit code sequences` for any
`visit code` with unscheduled appointments for the given subject
and schedule.
Wrapper for function `reset_visit_code_sequence_or_pass`.
"""
ann = (
get_appointment_model_cls()
.objects.values("visit_code")
.filter(subject_identifier=subject_identifier, visit_code_sequence__gt=0)
.annotate(Count("visit_code"))
)
for visit_code in [obj.get("visit_code") for obj in ann]:
reset_visit_code_sequence_or_pass(
subject_identifier=subject_identifier,
visit_schedule_name=visit_schedule_name,
schedule_name=schedule_name,
visit_code=visit_code,
)


def delete_appointment_in_sequence(appointment: Any, from_post_delete=None) -> None:
Expand Down
8 changes: 8 additions & 0 deletions edc_appointment/view_mixins/appointment_view_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
NEW_APPT,
SKIPPED_APPT,
)
from ..utils import reset_visit_code_sequence_for_subject

if TYPE_CHECKING:
from django.db.models import QuerySet
Expand Down Expand Up @@ -49,6 +50,13 @@ def get_context_data(self, **kwargs) -> dict[str, Any]:
if self.appointment.related_visit:
report_datetime = self.appointment.related_visit.report_datetime
kwargs.update(report_datetime=report_datetime)
else:
reset_visit_code_sequence_for_subject(
subject_identifier=self.subject_identifier,
visit_schedule_name=self.current_visit_schedule.name,
schedule_name=self.current_schedule.name,
)

has_call_manager = True if django_apps.app_configs.get("edc_call_manager") else False
kwargs.update(
appointment=self.appointment,
Expand Down
3 changes: 2 additions & 1 deletion edc_appointment_app/tests/appointment_app_test_case_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ def create_unscheduled_appointments(self, appointment):
appointment.appt_status = INCOMPLETE_APPT
appointment.save_base(update_fields=["appt_status"])

@staticmethod
def create_related_visit(
self, appointment: Appointment, reason: str | None = None
appointment: Appointment, reason: str | None = None
) -> SubjectVisit:
if not appointment.related_visit:
related_visit = SubjectVisit.objects.create(
Expand Down

0 comments on commit 5fcbdeb

Please sign in to comment.