Skip to content

Commit

Permalink
Worked: outgoing messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Filienko committed Feb 18, 2024
1 parent 1dc3ab4 commit 012a402
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 68 deletions.
133 changes: 65 additions & 68 deletions isacc_messaging/api/isacc_record_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ def __init__(self):
def dispatch_cr(self, cr: CommunicationRequest):
if cr.dispatched():
return cr.dispatched_message_status()
# Default succesfull status and statusReason
status = ""
statusReason = ""
target_phone = resolve_reference(cr.recipient[0].reference).get_phone_number()
Expand Down Expand Up @@ -234,22 +233,20 @@ def on_twilio_message_status_update(self, values):
}))
if existing_comm is None:
# Callback only occurs on completed Communications
c = cr.create_communication_from_request(status="completed")
c = Communication(c)
result = HAPI_request('POST', 'Communication', resource=c.as_json())
comm_json = cr.create_communication_from_request(status="completed")
result = HAPI_request('POST', 'Communication', resource=comm_json)
audit_entry(
f"Created Communication resource on Twilio callback:",
extra={"resource": result},
level='debug'
)
# if this was a manual message, mark patient as having been followed up with
if c.is_manual_follow_up_message():
if comm.is_manual_follow_up_message():
patient.mark_followup_extension()
else:
# Update the status of the communication to completed
comm = Communication(existing_comm)
comm.status = "completed"
result = comm.persist()
comm.change_status(status="completed")
audit_entry(
f"Received /MessageStatus callback with status {message_status} on existing Communication resource",
extra={"resource": result,
Expand All @@ -269,7 +266,6 @@ def on_twilio_message_status_update(self, values):

# maintain next outgoing and last followed up Twilio message
# extensions after each send (now know to be complete)
patient.mark_next_outgoing()
patient.mark_followup_extension()

def on_twilio_message_received(self, values):
Expand Down Expand Up @@ -326,7 +322,6 @@ def execute_requests(self) -> Tuple[List[dict], List[dict]]:
"""
successes = []
errors = []
skipped_crs = []

now = datetime.now().astimezone()
cutoff = now - timedelta(days=2)
Expand All @@ -339,80 +334,82 @@ def execute_requests(self) -> Tuple[List[dict], List[dict]]:

for cr_json in next_in_bundle(result):
cr = CommunicationRequest(cr_json)
# as that message was likely the next-outgoing for the patient,
# update the extension used to track next-outgoing-message time
patient = resolve_reference(cr.recipient[0].reference)
# Happens when the patient removes their phone number completely.
# Should not occur in production.
try:
patient_unsubscribed = any(
telecom_entry.system.lower() == 'sms' and telecom_entry.period.end
for telecom_entry in patient.telecom
)
except Exception as e:
skipped_crs.append({cr, False, "No Phone Number Registered"})
continue
patient.mark_next_outgoing()

if cr.occurrenceDateTime.date < cutoff:
# Anything older than cutoff will never be sent (#1861758)
# and needs a status adjustment lest it throws off other queries
# like next outgoing message time
skipped_crs.append((cr, "aborted", "Past the cutoff"))
# Do not interact with CR if the patient is inactive or sending date is past the cutoff
if not patient.active or cr.occurrenceDateTime.date < cutoff:
cr.status = "revoked"
cr.persist()
revoked_reason = ""
if not patient.active:
revoked_reason = "Recipient is not active"
else:
revoked_reason = "Past the cutoff"
cr.report_cr_status(status_reason=revoked_reason)
errors.append({'id': cr.id, 'error': revoked_reason})
continue
if patient_unsubscribed or not patient.active:
revoked_status = (cr, "aborted", 'Recipient is not active')
if patient_unsubscribed:
revoked_status = (cr, "stopped", 'Recipient unsubscribed')
skipped_crs.append(revoked_status)
# Do not cancel future sms

# Otherwise, create a communication
comm_json = cr.create_communication_from_request(status="in-progress")
updated_comm = HAPI_request('POST', 'Communication', resource=comm_json)
comm = Communication(updated_comm)
audit_entry(
f"Generated an {comm.status} Communication/{comm.id} for CommunicationRequest/{cr.id}",
extra={"resource": updated_comm},
level="debug"
)
# Update the CommunicationRequest as completed, since new Communication was sent
cr.status = "completed"
cr.persist()

# If patient unsubscribed, mark as stopped
if any(
telecom_entry.system.lower() == 'sms' and telecom_entry.period.end
for telecom_entry in getattr(patient, 'telecom', [])
):
comm.change_status(status="stopped")
errors.append({'id': cr.id, 'error': "Patient unsubscribed"})
audit_entry(
f"Updated {comm} to {comm.status}, because patient unsubscribed",
extra={"resource": comm,},
level='debug'
)
continue

# Otherwise, update according to the feedback from the dispatch
try:
comm_status, comm_statusReason = self.process_cr(cr, successes)
c = cr.create_communication_from_request(status=comm_status)
c = Communication(c)
HAPI_request('POST', 'Communication', resource=c.as_json())
comm.change_status(status=comm_status)
audit_entry(
f"Sent a message for {cr.id}. Status of Communication: {comm_status}",
extra={"resource": f"CommunicationResource/{cr.id}", "feedback": comm_statusReason},
level='exception'
f"Updated status of Communication/{comm.id} to {comm_status}",
extra={"resource": f"Communication/{comm.id}", "statusReason": comm_statusReason},
level='debug'
)
if comm_status == "in-progress":
# In-progress status entails that sms was successfully dispatched
successes.append({'id': cr.id, 'status': comm_statusReason})
else:
# Register an error encountered when sending a message
audit_entry(
f"Failed to send the message for CommunicationRequest/{cr.id} because {comm_statusReason}",
extra={"resource": f"Communication/{comm.id}", "statusReason": comm_status},
level='exception'
)

except Exception as e:
# Register an error when sending a message
comm.change_status(status="unknown")
audit_entry(
"Failed to send the message",
extra={"resource": f"CommunicationResource/{cr.id}", "exception": e},
f"Failed to send the message for CommunicationRequest/{cr.id} because {e}",
extra={"resource": f"Communication/{comm.id}", "statusReason": e},
level='exception'
)
skipped_crs.append((cr, "unknown", str(e)))

for cr, revoked_reason, e in skipped_crs:
# Aborted class marking CRs that should not be send
if revoked_reason != "aborted":
c = cr.create_communication_from_request(status=revoked_reason)
c = Communication(c)
result = HAPI_request('POST', 'Communication', resource=c.as_json())
audit_entry(
f"Generated new Communication for a revoked CR. Reason: {e}",
extra={"resource": f"{result}"},
level='debug'
)
cr.status = "revoked"
HAPI_request(
"PUT",
"CommunicationRequest",
resource_id=cr.id,
resource=cr.as_json())
audit_entry(
f"Skipped CommunicationRequest({cr.id}); status set to {cr.status} because {e}",
extra={"CommunicationRequest": cr.as_json(), "reason": revoked_reason, "exception": e})
errors.append({'id': cr.id, 'error': str(e)})
# as that message was likely the next-outgoing for the patient,
# update the extension used to track next-outgoing-message time
patient = resolve_reference(cr.recipient[0].reference)
patient.mark_next_outgoing()

return successes, errors

def process_cr(self, cr: CommunicationRequest, successes: list):
status, statusReason = self.dispatch_cr(cr=cr)
# In-progress status entails that sms was successfully dispatched
if status == "in-progress":
successes.append({'id': cr.id, 'status': statusReason})
return status, statusReason
6 changes: 6 additions & 0 deletions isacc_messaging/models/isacc_communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ def persist(self):
response = HAPI_request('PUT', 'Communication', resource_id=self.id, resource=self.as_json())
return response

def change_status(self, status):
"""Persist self state to FHIR store"""
self.status = status
response = self.persist()
return response

@staticmethod
def about_patient(patient):
"""Query for "outside" Communications about the patient
Expand Down
7 changes: 7 additions & 0 deletions isacc_messaging/models/isacc_communicationrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import datetime
from fhirclient.models.communicationrequest import CommunicationRequest
from fhirclient.models.identifier import Identifier
from isacc_messaging.audit import audit_entry

from isacc_messaging.models.fhir import HAPI_request, first_in_bundle

Expand Down Expand Up @@ -105,3 +106,9 @@ def persist(self):
"""Persist self state to FHIR store"""
response = HAPI_request('PUT', 'CommunicationRequest', resource_id=self.id, resource=self.as_json())
return response

def report_cr_status(self, status_reason):
audit_entry(
f"CommunicationRequest({self.id}) status set to {self.status} because {status_reason}",
extra={"CommunicationRequest": self.as_json(), "reason": status_reason}
)

0 comments on commit 012a402

Please sign in to comment.