Skip to content

Commit

Permalink
Drop FlowRun.session_id
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanseymour committed Feb 26, 2025
1 parent 8f4cc9e commit 1ed0b57
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 90 deletions.
2 changes: 1 addition & 1 deletion temba/flows/management/commands/undo_footgun.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def handle(self, start_id: int, event_types: list, dry_run: bool, quiet: bool, *

# process runs in batches
for run_id_batch in itertools.batched(run_ids, self.batch_size):
run_batch = list(FlowRun.objects.filter(id__in=run_id_batch).only("id", "contact_id", "session_id"))
run_batch = list(FlowRun.objects.filter(id__in=run_id_batch).only("id", "contact_id", "session_uuid"))

self.undo_for_batch(run_batch, undoers, dry_run)
num_fixed += len(run_batch)
Expand Down
17 changes: 17 additions & 0 deletions temba/flows/migrations/0380_remove_flowrun_session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1.4 on 2025-02-26 22:21

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("flows", "0379_remove_flowrun_flows_run_active_or_waiting_has_session"),
]

operations = [
migrations.RemoveField(
model_name="flowrun",
name="session",
),
]
3 changes: 0 additions & 3 deletions temba/flows/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1137,9 +1137,6 @@ class FlowRun(models.Model):
# current node location of this run in the flow
current_node_uuid = models.UUIDField(null=True)

# TODO drop
session = models.ForeignKey(FlowSession, on_delete=models.PROTECT, null=True)

@dataclass
class Step:
node: UUID
Expand Down
8 changes: 2 additions & 6 deletions temba/flows/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from temba.utils.crons import cron_task
from temba.utils.models import delete_in_batches

from .models import FlowActivityCount, FlowResultCount, FlowRevision, FlowRun, FlowSession, FlowStartCount
from .models import FlowActivityCount, FlowResultCount, FlowRevision, FlowSession, FlowStartCount

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -45,10 +45,6 @@ def trim_flow_sessions():

trim_before = timezone.now() - settings.RETENTION_PERIODS["flowsession"]

def pre_delete(session_ids):
# detach any flows runs that belong to these sessions
FlowRun.objects.filter(session_id__in=session_ids).update(session_id=None)

num_deleted = delete_in_batches(FlowSession.objects.filter(ended_on__lte=trim_before), pre_delete=pre_delete)
num_deleted = delete_in_batches(FlowSession.objects.filter(ended_on__lte=trim_before))

return {"deleted": num_deleted}
2 changes: 1 addition & 1 deletion temba/flows/tests/test_counts.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ def create_runs(flow_status_pairs: tuple) -> list:
FlowRun(
uuid=uuid4(),
org=self.org,
session=session,
flow=flow,
contact=contact,
status=status,
session_uuid=session.uuid,
created_on=timezone.now(),
modified_on=timezone.now(),
exited_on=timezone.now() if status not in ("A", "W") else None,
Expand Down
84 changes: 15 additions & 69 deletions temba/flows/tests/test_session.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from datetime import datetime, timezone as tzone
from datetime import datetime, timedelta, timezone as tzone

from django.utils import timezone

from temba.flows.models import FlowRun, FlowSession
from temba.flows.models import FlowSession
from temba.flows.tasks import trim_flow_sessions
from temba.tests import TembaTest
from temba.utils.uuid import uuid4
Expand All @@ -11,92 +11,38 @@
class FlowSessionTest(TembaTest):
def test_trim(self):
contact = self.create_contact("Ben Haggerty", phone="+250788123123")
flow = self.create_flow("Test")

# create some runs that have sessions
session1 = FlowSession.objects.create(
uuid=uuid4(),
contact=contact,
output_url="http://sessions.com/123.json",
status=FlowSession.STATUS_WAITING,
status=FlowSession.STATUS_COMPLETED,
ended_on=datetime(2025, 1, 15, 0, 0, 0, 0, tzone.utc),
)
session2 = FlowSession.objects.create(
uuid=uuid4(),
contact=contact,
output_url="http://sessions.com/234.json",
status=FlowSession.STATUS_WAITING,
status=FlowSession.STATUS_COMPLETED,
ended_on=datetime(2025, 1, 16, 0, 0, 0, 0, tzone.utc),
)
session3 = FlowSession.objects.create(
uuid=uuid4(),
contact=contact,
output_url="http://sessions.com/345.json",
status=FlowSession.STATUS_WAITING,
)
run1 = FlowRun.objects.create(
org=self.org,
flow=flow,
contact=contact,
session=session1,
session_uuid=session1.uuid,
status=FlowRun.STATUS_WAITING,
)
run2 = FlowRun.objects.create(
org=self.org,
flow=flow,
contact=contact,
session=session2,
session_uuid=session2.uuid,
status=FlowRun.STATUS_WAITING,
)
run3 = FlowRun.objects.create(
org=self.org,
flow=flow,
session4 = FlowSession.objects.create(
uuid=uuid4(),
contact=contact,
session=session3,
session_uuid=session3.uuid,
status=FlowRun.STATUS_WAITING,
output_url="http://sessions.com/345.json",
status=FlowSession.STATUS_COMPLETED,
ended_on=timezone.now() - timedelta(days=3),
)

# create an IVR call with session
call = self.create_incoming_call(flow, contact)
run4 = FlowRun.objects.get(session_uuid=call.session_uuid)

self.assertIsNotNone(run1.session)
self.assertIsNotNone(run2.session)
self.assertIsNotNone(run3.session)
self.assertIsNotNone(run4.session)

# end run1 and run4's sessions in the past
run1.status = FlowRun.STATUS_COMPLETED
run1.exited_on = datetime(2015, 9, 15, 0, 0, 0, 0, tzone.utc)
run1.save(update_fields=("status", "exited_on"))
run1.session.status = FlowSession.STATUS_COMPLETED
run1.session.ended_on = datetime(2015, 9, 15, 0, 0, 0, 0, tzone.utc)
run1.session.save(update_fields=("status", "ended_on"))

run4.status = FlowRun.STATUS_INTERRUPTED
run4.exited_on = datetime(2015, 9, 15, 0, 0, 0, 0, tzone.utc)
run4.save(update_fields=("status", "exited_on"))
run4.session.status = FlowSession.STATUS_INTERRUPTED
run4.session.ended_on = datetime(2015, 9, 15, 0, 0, 0, 0, tzone.utc)
run4.session.save(update_fields=("status", "ended_on"))

# end run2's session now
run2.status = FlowRun.STATUS_EXPIRED
run2.exited_on = timezone.now()
run2.save(update_fields=("status", "exited_on"))
run4.session.status = FlowSession.STATUS_COMPLETED
run2.session.ended_on = timezone.now()
run2.session.save(update_fields=("status", "ended_on"))

trim_flow_sessions()

run1, run2, run3, run4 = FlowRun.objects.order_by("id")

self.assertIsNone(run1.session)
self.assertIsNotNone(run2.session) # ended too recently to be deleted
self.assertIsNotNone(run3.session) # never ended
self.assertIsNone(run4.session)

# only sessions for run2 and run3 are left
self.assertEqual(FlowSession.objects.count(), 2)
self.assertFalse(FlowSession.objects.filter(id=session1.id).exists())
self.assertFalse(FlowSession.objects.filter(id=session2.id).exists())
self.assertTrue(FlowSession.objects.filter(id=session3.id).exists()) # not ended
self.assertTrue(FlowSession.objects.filter(id=session4.id).exists()) # ended too recently
1 change: 0 additions & 1 deletion temba/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,6 @@ def create_incoming_call(
flow=flow,
contact=contact,
status=FlowRun.STATUS_COMPLETED,
session=session,
session_uuid=session.uuid,
exited_on=timezone.now(),
)
Expand Down
19 changes: 10 additions & 9 deletions temba/tests/mailroom.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,12 @@ def inspect(c) -> dict:

@_client_method
def contact_interrupt(self, org, user, contact) -> int:
# get the waiting session IDs
session_ids = list(contact.sessions.filter(status=FlowSession.STATUS_WAITING).values_list("id", flat=True))
# get the waiting session UUIDs
session_uuids = list(contact.sessions.filter(status=FlowSession.STATUS_WAITING).values_list("uuid", flat=True))

exit_sessions(session_ids, FlowSession.STATUS_INTERRUPTED)
exit_sessions(session_uuids, FlowSession.STATUS_INTERRUPTED)

return len(session_ids)
return len(session_uuids)

@_client_method
def contact_parse_query(self, org, query: str, parse_only: bool = False):
Expand Down Expand Up @@ -854,20 +854,21 @@ def find_boundary_by_name(org, name, level, parent):
return boundary


def exit_sessions(session_ids: list, status: str):
FlowRun.objects.filter(session_id__in=session_ids).update(
def exit_sessions(session_uuids: list, status: str):
FlowRun.objects.filter(session_uuid__in=session_uuids).update(
status=status, exited_on=timezone.now(), modified_on=timezone.now()
)
FlowSession.objects.filter(id__in=session_ids).update(
FlowSession.objects.filter(uuid__in=session_uuids).update(
status=status,
ended_on=timezone.now(),
current_flow_id=None,
)

for session in FlowSession.objects.filter(id__in=session_ids):
for session in FlowSession.objects.filter(uuid__in=session_uuids):
session.contact.current_session_uuid = None
session.contact.current_flow = None
session.contact.modified_on = timezone.now()
session.contact.save(update_fields=("current_flow", "modified_on"))
session.contact.save(update_fields=("current_session_uuid", "current_flow", "modified_on"))


def resolve_destination(org, contact, channel=None) -> tuple:
Expand Down

0 comments on commit 1ed0b57

Please sign in to comment.