From fa788c0b6790bc9c908dbb7c91b700ee7559e7fa Mon Sep 17 00:00:00 2001
From: Paul Bugni <pbugni@u.washington.edu>
Date: Tue, 7 Jan 2025 14:10:28 -0800
Subject: [PATCH 1/6] Pivotal story #188742640, need to `mark_next_outgoing()`
 AFTER changing status of communication request.

---
 isacc_messaging/api/isacc_record_creator.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/isacc_messaging/api/isacc_record_creator.py b/isacc_messaging/api/isacc_record_creator.py
index e72f5ea..dbc8803 100644
--- a/isacc_messaging/api/isacc_record_creator.py
+++ b/isacc_messaging/api/isacc_record_creator.py
@@ -342,10 +342,7 @@ def execute_requests(self) -> Tuple[List[dict], List[dict]]:
         sent = 0
         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)
-            patient.mark_next_outgoing()
 
             # 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:
@@ -358,6 +355,7 @@ def execute_requests(self) -> Tuple[List[dict], List[dict]]:
                     revoked_reason = "Past the cutoff"
                 cr.report_cr_status(status_reason=revoked_reason)
                 errors.append({'id': cr.id, 'error': revoked_reason})
+                patient.mark_next_outgoing()  # update given state change
                 continue
 
             # Otherwise, create a communication
@@ -384,6 +382,7 @@ def execute_requests(self) -> Tuple[List[dict], List[dict]]:
                     extra={"Updated Communication": stopped_comm},
                     level='debug'
                 )
+                patient.mark_next_outgoing()  # update given state change
                 continue
 
             # Otherwise, update according to the feedback from the dispatch
@@ -419,6 +418,7 @@ def execute_requests(self) -> Tuple[List[dict], List[dict]]:
                     extra={"resource": f"Communication/{comm.id}", "statusReason": e},
                     level='exception'
                 )
+            patient.mark_next_outgoing()  # update given state change
 
             # Flooding system on occasions such as a holiday message to all,
             # leads to an overwhelmed system.  Restrict the flood by processing

From 943ce9f6f02522d6cd61c9083b62dba76e4bcdbc Mon Sep 17 00:00:00 2001
From: Paul Bugni <pbugni@u.washington.edu>
Date: Tue, 7 Jan 2025 15:12:29 -0800
Subject: [PATCH 2/6] no change code warning clean up.

---
 isacc_messaging/api/isacc_record_creator.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/isacc_messaging/api/isacc_record_creator.py b/isacc_messaging/api/isacc_record_creator.py
index dbc8803..162d11f 100644
--- a/isacc_messaging/api/isacc_record_creator.py
+++ b/isacc_messaging/api/isacc_record_creator.py
@@ -348,11 +348,9 @@ def execute_requests(self) -> Tuple[List[dict], List[dict]]:
             if not patient.active or cr.occurrenceDateTime.date < cutoff:
                 cr.status = "revoked"
                 cr.persist()
-                revoked_reason = ""
+                revoked_reason = "Past the cutoff"
                 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})
                 patient.mark_next_outgoing()  # update given state change

From 8b8618dcfb40c196f2dbd49d91407ee8272eeed9 Mon Sep 17 00:00:00 2001
From: Paul Bugni <pbugni@u.washington.edu>
Date: Tue, 7 Jan 2025 15:41:36 -0800
Subject: [PATCH 3/6] need to update all active users next-outgoing extension
 (and not touch the followup values)

---
 isacc_messaging/api/views.py | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/isacc_messaging/api/views.py b/isacc_messaging/api/views.py
index 3f63b9b..62906fb 100644
--- a/isacc_messaging/api/views.py
+++ b/isacc_messaging/api/views.py
@@ -275,18 +275,19 @@ def send_system_emails(category, dry_run, include_test_patients):
 @click.option("--dry-run", is_flag=True, default=False, help="Simulate execution; don't persist to FHIR store")
 def update_patient_extensions(dry_run):
     """Iterate through active patients, update any stale/missing extensions"""
-    # this was a 1 and done migration method.  disable for now
-    raise click.ClickException(
-        "DISABLED: unsafe to run as this will now undo any user marked "
-        "read messages via "
-        "https://github.com/uwcirg/isacc-messaging-client-sof/pull/85")
+    # not routinely used - typically following a bug fix WRT user extensions
+
+    # NB - no longer updating the followup-extension as it would displace
+    # any work done by users who "marked as read" the latest message for a patient
+    # see https://github.com/uwcirg/isacc-messaging-client-sof/pull/85
+
     from isacc_messaging.models.fhir import next_in_bundle
     from isacc_messaging.models.isacc_patient import IsaccPatient as Patient
     active_patients = Patient.active_patients()
     for json_patient in next_in_bundle(active_patients):
         patient = Patient(json_patient)
         patient.mark_next_outgoing(persist_on_change=not dry_run)
-        patient.mark_followup_extension(persist_on_change=not dry_run)
+        # (see note above) patient.mark_followup_extension(persist_on_change=not dry_run)
 
 
 @base_blueprint.cli.command("maintenance-reinstate-all-patients")

From 394dd3f3bfee05ca7fc1f30c6532244f7fa0f72b Mon Sep 17 00:00:00 2001
From: Paul F Bugni <pbugni@u.washington.edu>
Date: Tue, 7 Jan 2025 17:21:46 -0800
Subject: [PATCH 4/6] Add migration to recalculate the next-outgoing extension
 for all active patients.

---
 .../recalculate-next-outgoing-times.py        | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 migrations/versions/recalculate-next-outgoing-times.py

diff --git a/migrations/versions/recalculate-next-outgoing-times.py b/migrations/versions/recalculate-next-outgoing-times.py
new file mode 100644
index 0000000..3a016c2
--- /dev/null
+++ b/migrations/versions/recalculate-next-outgoing-times.py
@@ -0,0 +1,24 @@
+# Migration script generated for recalculate-next-outgoing-times
+revision = '17f3b60d-0777-4d0e-bb32-82e670b573a7'
+down_revision = 'None'
+
+import logging
+from isacc_messaging.models.fhir import next_in_bundle
+from isacc_messaging.models.isacc_patient import IsaccPatient as Patient
+
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
+
+
+def upgrade():
+    # iterate over active patients; confirm/set next-outgoing extension
+    active_patients = Patient.active_patients()
+    for json_patient in next_in_bundle(active_patients):
+        patient = Patient(json_patient)
+        patient.mark_next_outgoing(persist_on_change=True)
+    logging.info('corrected next-outgoing-times')
+
+
+def downgrade():
+    # Nothing to undo
+    logging.info('downgraded')
+

From 23d3549a5ed964222cd6ab82e75f57ff9710bbaf Mon Sep 17 00:00:00 2001
From: Paul F Bugni <pbugni@u.washington.edu>
Date: Tue, 7 Jan 2025 19:30:52 -0800
Subject: [PATCH 5/6] Run `flack upgrade` when starting the messagingservice

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 5c84d1c..6aa19c6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,4 +15,4 @@ ENV FLASK_APP=isacc_messaging.app:create_app() \
 
 EXPOSE "${PORT}"
 
-CMD gunicorn --bind "0.0.0.0:${PORT:-8000}" ${FLASK_APP}
+CMD flask upgrade && gunicorn --bind "0.0.0.0:${PORT:-8000}" ${FLASK_APP}

From b46af325f8264f6fa857ab8b4de494cc37917f90 Mon Sep 17 00:00:00 2001
From: Paul F Bugni <pbugni@u.washington.edu>
Date: Tue, 7 Jan 2025 20:04:54 -0800
Subject: [PATCH 6/6] Revert "need to update all active users next-outgoing
 extension (and not touch the followup values)"

This reverts commit 5a2de5410d558fb3e407f6b7d240d3610c837eda.
---
 isacc_messaging/api/views.py | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/isacc_messaging/api/views.py b/isacc_messaging/api/views.py
index 62906fb..3f63b9b 100644
--- a/isacc_messaging/api/views.py
+++ b/isacc_messaging/api/views.py
@@ -275,19 +275,18 @@ def send_system_emails(category, dry_run, include_test_patients):
 @click.option("--dry-run", is_flag=True, default=False, help="Simulate execution; don't persist to FHIR store")
 def update_patient_extensions(dry_run):
     """Iterate through active patients, update any stale/missing extensions"""
-    # not routinely used - typically following a bug fix WRT user extensions
-
-    # NB - no longer updating the followup-extension as it would displace
-    # any work done by users who "marked as read" the latest message for a patient
-    # see https://github.com/uwcirg/isacc-messaging-client-sof/pull/85
-
+    # this was a 1 and done migration method.  disable for now
+    raise click.ClickException(
+        "DISABLED: unsafe to run as this will now undo any user marked "
+        "read messages via "
+        "https://github.com/uwcirg/isacc-messaging-client-sof/pull/85")
     from isacc_messaging.models.fhir import next_in_bundle
     from isacc_messaging.models.isacc_patient import IsaccPatient as Patient
     active_patients = Patient.active_patients()
     for json_patient in next_in_bundle(active_patients):
         patient = Patient(json_patient)
         patient.mark_next_outgoing(persist_on_change=not dry_run)
-        # (see note above) patient.mark_followup_extension(persist_on_change=not dry_run)
+        patient.mark_followup_extension(persist_on_change=not dry_run)
 
 
 @base_blueprint.cli.command("maintenance-reinstate-all-patients")