From d36c6611fb933ad1ae12b2bdda2fc0dbd20a0f3d Mon Sep 17 00:00:00 2001 From: erikvw Date: Fri, 22 Sep 2023 21:00:37 -0500 Subject: [PATCH] update for changes in edc-appointment --- .../form_validators/form_validator_mixins.py | 9 +++ edc_next_appointment/modelform_mixins.py | 66 +++++++++---------- .../tests/tests/test_next_appointment.py | 30 ++++----- next_appointment_app/consents.py | 4 +- runtests.py | 4 +- 5 files changed, 60 insertions(+), 53 deletions(-) diff --git a/edc_next_appointment/form_validators/form_validator_mixins.py b/edc_next_appointment/form_validators/form_validator_mixins.py index cf6c458..2fb6064 100644 --- a/edc_next_appointment/form_validators/form_validator_mixins.py +++ b/edc_next_appointment/form_validators/form_validator_mixins.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from django.utils.translation import gettext_lazy as _ +from edc_appointment.utils import get_allow_skipped_appt_using from edc_form_validators import INVALID_ERROR from edc_utils.date import to_local @@ -20,6 +21,14 @@ def __init__(self, **kwargs): self.day_abbr = calendar.weekheader(3).split(" ") super().__init__(**kwargs) + @property + def visit_code_fld(self): + return get_allow_skipped_appt_using().get(self._meta.model._meta.label_lower)[1] + + @property + def dt_fld(self): + return get_allow_skipped_appt_using().get(self._meta.model._meta.label_lower)[1] + @property def clinic_days(self) -> list[int]: if not self._clinic_days: diff --git a/edc_next_appointment/modelform_mixins.py b/edc_next_appointment/modelform_mixins.py index 7a81fad..0c333f0 100644 --- a/edc_next_appointment/modelform_mixins.py +++ b/edc_next_appointment/modelform_mixins.py @@ -16,21 +16,29 @@ class NextAppointmentModelFormMixin: def clean(self): cleaned_data = super().clean() - self.validate_appt_date_with_next() - self.validate_best_next_visit_code() + self.validate_suggested_date_with_future_appointments() + self.validate_suggested_visit_code() return cleaned_data @property - def next_appt_date(self) -> date | None: + def suggested_date(self) -> date | None: return self.cleaned_data.get("appt_date") - def validate_appt_date_with_next(self): - if self.next_appt_date and self.related_visit.appointment.next.appt_status not in [ + @property + def suggested_visit_code(self) -> str | None: + return getattr( + self.cleaned_data.get("visitschedule"), + "visit_code", + self.cleaned_data.get("visitschedule"), + ) + + def validate_suggested_date_with_future_appointments(self): + if self.suggested_date and self.related_visit.appointment.next.appt_status not in [ NEW_APPT, SKIPPED_APPT, ]: if ( - self.next_appt_date + self.suggested_date != to_local(self.related_visit.appointment.next.appt_datetime).date() ): next_appt = self.related_visit.appointment.next @@ -40,19 +48,19 @@ def validate_appt_date_with_next(self): { "appt_date": _( "Invalid. Next visit report already submitted. Expected " - "`%(next_appt_date)s`. See `%(next_appt_visit_code)s`." + "`%(dt)s`. See `%(visit_code)s`." ) % { - "next_appt_date": next_appt_date, - "next_appt_visit_code": next_appt.visit_code, + "dt": next_appt_date, + "visit_code": next_appt.visit_code, } } ) if ( - self.next_appt_date + self.suggested_date and self.related_visit.appointment.next.appt_status not in [NEW_APPT, SKIPPED_APPT] - and self.next_appt_date + and self.suggested_date > to_local(self.related_visit.appointment.next.appt_datetime).date() ): next_appt = self.related_visit.appointment.next @@ -60,25 +68,25 @@ def validate_appt_date_with_next(self): raise forms.ValidationError( { "appt_date": _( - "Invalid. Expected a date before next appointment " - "`%(next_appt_visit_code)s` on " - "%(next_appt_date_str)s." + "Invalid. Expected a date before appointment " + "`%(visit_code)s` on " + "%(dt_str)s." ) % { - "next_appt_visit_code": next_appt.visit_code, - "next_appt_date_str": to_local(next_appt.appt_datetime) + "visit_code": next_appt.visit_code, + "dt_str": to_local(next_appt.appt_datetime) .date() .strftime(date_format), } } ) - def validate_best_next_visit_code(self): - if appt_date := self.next_appt_date: + def validate_suggested_visit_code(self): + if suggested_date := self.suggested_date: subject_visit = self.cleaned_data.get("subject_visit") try: appointment = get_appointment_by_datetime( - self.as_appt_datetime(appt_date), + self.as_datetime(suggested_date), subject_identifier=subject_visit.subject_identifier, visit_schedule_name=subject_visit.visit_schedule.name, schedule_name=subject_visit.schedule.name, @@ -103,30 +111,22 @@ def validate_best_next_visit_code(self): ) if ( - self.cleaned_data.get("visitschedule") - and self.cleaned_data.get("visitschedule").visit_code != appointment.visit_code + self.suggested_visit_code + and self.suggested_visit_code != appointment.visit_code ): date_format = convert_php_dateformat(settings.SHORT_DATE_FORMAT) raise forms.ValidationError( { "visitschedule": _( - "Expected %(visit_code)s using %(appt_date_str)s from above." + "Expected %(visit_code)s using %(dt_str)s from above." ) % { "visit_code": appointment.visit_code, - "appt_date_str": appt_date.strftime(date_format), + "dt_str": suggested_date.strftime(date_format), } } ) @staticmethod - def as_appt_datetime(appt_date: date) -> datetime: - return datetime( - appt_date.year, - appt_date.month, - appt_date.day, - 23, - 59, - 59, - tzinfo=ZoneInfo("UTC"), - ) + def as_datetime(dt: date) -> datetime: + return datetime(dt.year, dt.month, dt.day, 23, 59, 59, tzinfo=ZoneInfo("UTC")) diff --git a/edc_next_appointment/tests/tests/test_next_appointment.py b/edc_next_appointment/tests/tests/test_next_appointment.py index a472281..0f76519 100644 --- a/edc_next_appointment/tests/tests/test_next_appointment.py +++ b/edc_next_appointment/tests/tests/test_next_appointment.py @@ -5,7 +5,7 @@ from dateutil.relativedelta import relativedelta from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist -from django.test import TestCase, override_settings, tag +from django.test import TestCase, override_settings from edc_appointment.constants import SKIPPED_APPT from edc_appointment.models import Appointment from edc_constants.constants import NOT_APPLICABLE, PATIENT @@ -63,9 +63,9 @@ def setUp(self): ) @override_settings( - EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING=[ - ("next_appointment_app.nextappointment", "appt_date") - ] + EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING={ + "next_appointment_app.nextappointment": ("appt_date", "visitschedule") + } ) @time_machine.travel(dt.datetime(2019, 6, 11, 8, 00, tzinfo=utc)) def test_ok(self): @@ -74,11 +74,10 @@ def test_ok(self): subject_visit_model_cls = get_related_visit_model_cls() subject_visit_model_cls.objects.create(appointment=appointment, reason=SCHEDULED) - @tag("1") @override_settings( - EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING=[ - ("next_appointment_app.nextappointment", "appt_date") - ] + EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING={ + "next_appointment_app.nextappointment": ("appt_date", "visitschedule") + } ) @time_machine.travel(dt.datetime(2019, 6, 11, 8, 00, tzinfo=utc)) def test_next_appt_ok(self): @@ -95,8 +94,8 @@ def test_next_appt_ok(self): subject_visit=subject_visit, report_datetime=subject_visit.report_datetime, appt_date=(appointment.appt_datetime + relativedelta(months=3)).date(), - info_source=InfoSources.objects.get(name=PATIENT), visitschedule=VisitSchedule.objects.get(visit_code="1000"), + info_source=InfoSources.objects.get(name=PATIENT), ) form = NextAppointmentForm(data=data) form.is_valid() @@ -135,9 +134,9 @@ def test_next_appt_ok(self): self.assertEqual(apppointment.appt_timing, NOT_APPLICABLE) @override_settings( - EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING=[ - ("next_appointment_app.nextappointment", "appt_date") - ] + EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING={ + "next_appointment_app.nextappointment": ("appt_date", "visitschedule") + } ) @time_machine.travel(dt.datetime(2019, 6, 11, 8, 00, tzinfo=utc)) def test_next_appt_with_health_facility(self): @@ -196,11 +195,10 @@ def test_next_appt_with_health_facility(self): form.is_valid() self.assertEqual({}, form._errors) - @tag("1") @override_settings( - EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING=[ - ("next_appointment_app.nextappointment", "appt_date") - ], + EDC_APPOINTMENT_ALLOW_SKIPPED_APPT_USING={ + "next_appointment_app.nextappointment": ("appt_date", "visitschedule") + }, LANGUAGE_CODE="sw", ) @time_machine.travel(dt.datetime(2019, 6, 11, 8, 00, tzinfo=utc_tz)) diff --git a/next_appointment_app/consents.py b/next_appointment_app/consents.py index b988d5c..a755b3a 100644 --- a/next_appointment_app/consents.py +++ b/next_appointment_app/consents.py @@ -8,8 +8,8 @@ v1 = Consent( "next_appointment_app.subjectconsent", version="1", - start=datetime(2018, 1, 1, 0, 0, tzinfo=ZoneInfo("utc")), - end=datetime(2023, 1, 1, 0, 0, tzinfo=ZoneInfo("utc")), + start=datetime(2018, 1, 1, 0, 0, tzinfo=ZoneInfo("UTC")), + end=datetime(2023, 1, 1, 0, 0, tzinfo=ZoneInfo("UTC")), age_min=18, age_is_adult=18, age_max=110, diff --git a/runtests.py b/runtests.py index 9a8fcfc..ee254cb 100644 --- a/runtests.py +++ b/runtests.py @@ -17,8 +17,8 @@ ETC_DIR=os.path.join(base_dir, app_name, "tests", "etc"), SUBJECT_VISIT_MODEL="edc_visit_tracking.subjectvisit", SUBJECT_VISIT_MISSED_MODEL="edc_visit_tracking.subjectvisitmissed", - EDC_PROTOCOL_STUDY_OPEN_DATETIME=datetime(2018, 1, 1, 0, 0, tzinfo=ZoneInfo("utc")), - EDC_PROTOCOL_STUDY_CLOSE_DATETIME=datetime(2023, 1, 1, 0, 0, tzinfo=ZoneInfo("utc")), + EDC_PROTOCOL_STUDY_OPEN_DATETIME=datetime(2018, 1, 1, 0, 0, tzinfo=ZoneInfo("UTC")), + EDC_PROTOCOL_STUDY_CLOSE_DATETIME=datetime(2023, 1, 1, 0, 0, tzinfo=ZoneInfo("UTC")), INSTALLED_APPS=[ "django.contrib.admin", "django.contrib.auth",