Skip to content

Commit

Permalink
Merge branch 'release/0.4.16' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
erikvw committed Feb 1, 2024
2 parents c8874f1 + 073de36 commit 70111f3
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from datetime import datetime
from typing import TYPE_CHECKING

from dateutil.relativedelta import relativedelta
from django.conf import settings
from edc_utils import convert_php_dateformat, to_utc
from django.utils.translation import gettext as _
from edc_utils import convert_php_dateformat, formatted_date, to_utc
from edc_utils.date import ceil_datetime, floor_secs
from edc_visit_schedule.exceptions import (
ScheduledVisitWindowError,
Expand Down Expand Up @@ -57,22 +59,31 @@ def datetime_in_window_or_raise(
visit_code_sequence=appointment.visit_code_sequence,
baseline_timepoint_datetime=self.baseline_timepoint_datetime(appointment),
)
except UnScheduledVisitWindowError as e:
except UnScheduledVisitWindowError:
if not self.ignore_window_period_for_unscheduled(
appointment, proposed_appt_datetime
):
# TODO: fix the dates on this message to match e.message
lower = floor_secs(get_lower_datetime(appointment)).strftime(
datetimestring
)
upper = floor_secs(get_lower_datetime(appointment.next)).strftime(
datetimestring
)
lower = floor_secs(get_lower_datetime(appointment))
try:
# one day less than the next related_visit, if it exists
upper = floor_secs(
appointment.next.related_visit.report_datetime
- relativedelta(days=1)
)
except AttributeError:
# lower bound of next appointment
upper = floor_secs(get_lower_datetime(appointment.next))
dt_lower = formatted_date(lower)
dt_upper = formatted_date(upper)
self.raise_validation_error(
{
form_field: (
"Invalid. Expected a date/time between "
f"{lower} and {upper} (U). Got {e}."
_(
"Invalid. Expected a date between "
"%(dt_lower)s and %(dt_upper)s (U)."
)
% dict(dt_lower=dt_lower, dt_upper=dt_upper)
)
},
UNSCHEDULED_WINDOW_ERROR,
Expand Down
125 changes: 68 additions & 57 deletions edc_appointment/models/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,33 @@
AppointmentStatusUpdater,
AppointmentStatusUpdaterError,
)
from ..constants import IN_PROGRESS_APPT
from ..constants import IN_PROGRESS_APPT, NEW_APPT
from ..creators import create_next_appointment_as_interim
from ..managers import AppointmentDeleteError
from ..skip_appointments import SkipAppointments
from ..utils import (
cancelled_appointment,
get_allow_skipped_appt_using,
get_appointment_model_cls,
get_appointment_model_name,
missed_appointment,
update_unscheduled_appointment_sequence,
)
from .appointment import Appointment


@receiver(post_save, weak=False, dispatch_uid="appointment_post_save")
@receiver(post_save, sender=Appointment, weak=False, dispatch_uid="appointment_post_save")
def appointment_post_save(sender, instance, raw, update_fields, **kwargs):
if not raw and isinstance(instance, get_appointment_model_cls()):
if not update_fields:
# use the AppointmentStatusUpdater to set all
# other appointments to be NOT in progress
try:
if instance.appt_status == IN_PROGRESS_APPT:
AppointmentStatusUpdater(instance, clear_others_in_progress=True)
except AttributeError as e:
if "appt_status" not in str(e):
raise
except AppointmentStatusUpdaterError:
pass
if not raw and not update_fields:
# use the AppointmentStatusUpdater to set all
# other appointments to be NOT in progress
try:
if instance.appt_status == IN_PROGRESS_APPT:
AppointmentStatusUpdater(instance, clear_others_in_progress=True)
except AttributeError as e:
if "appt_status" not in str(e):
raise
except AppointmentStatusUpdaterError:
pass
# try to create_missed_visit_from_appointment
# if appt_status == missed
missed_appointment(instance)
Expand Down Expand Up @@ -78,58 +77,70 @@ def update_appt_status_on_related_visit_post_save(
pass


@receiver(pre_delete, weak=False, dispatch_uid="appointments_on_pre_delete")
@receiver(
post_delete, weak=False, dispatch_uid="update_appt_status_on_related_visit_post_delete"
)
def update_appt_status_on_related_visit_post_delete(sender, instance, using, **kwargs):
if isinstance(instance, (get_related_visit_model_cls(),)):
try:
appointment = instance.appointment
except AttributeError as e:
if "appointment" not in str(e):
raise
else:
appointment.appt_status = NEW_APPT
appointment.save_base(update_fields=["appt_status"])


@receiver(
pre_delete, sender=Appointment, weak=False, dispatch_uid="appointments_on_pre_delete"
)
def appointments_on_pre_delete(sender, instance, using, **kwargs):
if isinstance(instance, (get_appointment_model_cls(),)):
if instance.visit_code_sequence == 0:
schedule = site_visit_schedules.get_visit_schedule(
instance.visit_schedule_name
).schedules.get(instance.schedule_name)
onschedule_datetime = schedule.onschedule_model_cls.objects.get(
if instance.visit_code_sequence == 0:
schedule = site_visit_schedules.get_visit_schedule(
instance.visit_schedule_name
).schedules.get(instance.schedule_name)
onschedule_datetime = schedule.onschedule_model_cls.objects.get(
subject_identifier=instance.subject_identifier
).onschedule_datetime
try:
offschedule_datetime = schedule.offschedule_model_cls.objects.get(
subject_identifier=instance.subject_identifier
).onschedule_datetime
try:
offschedule_datetime = schedule.offschedule_model_cls.objects.get(
subject_identifier=instance.subject_identifier
).offschedule_datetime
except ObjectDoesNotExist:
).offschedule_datetime
except ObjectDoesNotExist:
raise AppointmentDeleteError(
f"Appointment may not be deleted. "
f"Subject {instance.subject_identifier} is on schedule "
f"'{instance.visit_schedule.verbose_name}.{instance.schedule_name}' "
f"as of '{formatted_datetime(onschedule_datetime)}'. "
f"Got appointment {instance.visit_code}.{instance.visit_code_sequence} "
f"datetime {formatted_datetime(instance.appt_datetime)}. "
f"Perhaps complete off schedule model "
f"'{instance.schedule.offschedule_model_cls().verbose_name.title()}' "
f"first."
)
else:
if onschedule_datetime <= instance.appt_datetime <= offschedule_datetime:
raise AppointmentDeleteError(
f"Appointment may not be deleted. "
f"Subject {instance.subject_identifier} is on schedule "
f"'{instance.visit_schedule.verbose_name}.{instance.schedule_name}' "
f"as of '{formatted_datetime(onschedule_datetime)}'. "
f"Got appointment {instance.visit_code}.{instance.visit_code_sequence} "
f"datetime {formatted_datetime(instance.appt_datetime)}. "
f"Perhaps complete off schedule model "
f"'{instance.schedule.offschedule_model_cls().verbose_name.title()}' "
f"first."
f"as of '{formatted_datetime(onschedule_datetime)}' "
f"until '{formatted_datetime(get_utcnow())}'. "
f"Got appointment datetime "
f"{formatted_datetime(instance.appt_datetime)}. "
)
else:
if onschedule_datetime <= instance.appt_datetime <= offschedule_datetime:
raise AppointmentDeleteError(
f"Appointment may not be deleted. "
f"Subject {instance.subject_identifier} is on schedule "
f"'{instance.visit_schedule.verbose_name}.{instance.schedule_name}' "
f"as of '{formatted_datetime(onschedule_datetime)}' "
f"until '{formatted_datetime(get_utcnow())}'. "
f"Got appointment datetime "
f"{formatted_datetime(instance.appt_datetime)}. "
)
else:
# TODO: any conditions for unscheduled?
pass


@receiver(post_delete, weak=False, dispatch_uid="appointments_on_post_delete")
@receiver(
post_delete, sender=Appointment, weak=False, dispatch_uid="appointments_on_post_delete"
)
def appointments_on_post_delete(sender, instance, using, **kwargs):
if isinstance(instance, (get_appointment_model_cls(),)):
if (
not kwargs.get("update_fields")
and sender._meta.label_lower == get_appointment_model_name()
):
update_unscheduled_appointment_sequence(
subject_identifier=instance.subject_identifier
)
if (
not kwargs.get("update_fields")
and sender._meta.label_lower == get_appointment_model_name()
):
update_unscheduled_appointment_sequence(subject_identifier=instance.subject_identifier)


@receiver(
Expand Down
Loading

0 comments on commit 70111f3

Please sign in to comment.