def end_flow_activity(self, org): self._log(" > Ending flow %s for in org %s\n" % (org.cache['activity']['flow'].name, org.name)) org.cache['activity'] = None runs = FlowRun.objects.filter(org=org, is_active=True) FlowRun.bulk_exit(runs, FlowRun.EXIT_TYPE_EXPIRED)
def apply_manual(): from temba.flows.models import Flow, FlowRun flows = Flow.objects.filter(is_archived=True) for flow in flows: runs = FlowRun.objects.filter(is_active=True, exit_type=None, flow_id=flow.id) FlowRun.bulk_exit(runs, FlowRun.EXIT_TYPE_INTERRUPTED)
def end_flow_activity(self, org): self._log(" > Ending flow %s for in org %s\n" % (org.cache["activity"]["flow"].name, org.name)) org.cache["activity"] = None runs = FlowRun.objects.filter(org=org, is_active=True) FlowRun.bulk_exit(runs, FlowRun.EXIT_TYPE_EXPIRED)
def remove_expired_flows_from_active(apps, schema_editor): r = get_redis_connection() for key in r.keys('*:step_active_set:*'): # make sure our flow run activity is removed FlowRun.do_expire_runs( FlowRun.objects.filter(pk__in=r.smembers(key), is_active=False, contact__is_test=False))
def remove_expired_flows_from_active(apps, schema_editor): r = get_redis_connection() for key in r.keys('*:step_active_set:*'): # make sure our flow run activity is removed runs = FlowRun.objects.filter(pk__in=r.smembers(key), is_active=False, contact__is_test=False) FlowRun.bulk_exit(runs, FlowRun.EXIT_TYPE_EXPIRED)
def save(self): started = self.validated_data["started"] steps = self.validated_data.get("steps", []) completed = self.validated_data.get("completed", False) revision = self.validated_data.get("revision", self.validated_data.get("version")) # look for previous run with this contact and flow run = ( FlowRun.objects.filter( org=self.org, contact=self.contact_obj, flow=self.flow_obj, created_on=started, is_active=True ) .order_by("-modified_on") .first() ) if not run or run.submitted_by != self.submitted_by_obj: run = FlowRun.create( self.flow_obj, self.contact_obj, created_on=started, submitted_by=self.submitted_by_obj ) run.update_from_surveyor(revision, steps) if completed: completed_on = steps[len(steps) - 1]["arrived_on"] if steps else None run.set_completed(exit_uuid=None, completed_on=completed_on) else: run.save(update_fields=("modified_on",)) return run
def save(self): started = self.validated_data['started'] steps = self.validated_data.get('steps', []) completed = self.validated_data.get('completed', False) # look for previous run with this contact and flow run = (FlowRun.objects.filter( org=self.org, contact=self.contact_obj, flow=self.flow_obj, created_on=started, is_active=True).order_by('-modified_on').first()) if not run or run.submitted_by != self.submitted_by_obj: run = FlowRun.create(self.flow_obj, self.contact_obj, created_on=started, submitted_by=self.submitted_by_obj) step_objs = [ FlowStep.from_json(step, self.flow_obj, run) for step in steps ] if completed: final_step = step_objs[len(step_objs) - 1] if step_objs else None completed_on = steps[len(steps) - 1]['arrived_on'] if steps else None run.set_completed(final_step, completed_on=completed_on) else: run.save(update_fields=('modified_on', )) return run
def save(self): started = self.validated_data["started"] steps = self.validated_data.get("steps", []) completed = self.validated_data.get("completed", False) # look for previous run with this contact and flow run = (FlowRun.objects.filter( org=self.org, contact=self.contact_obj, flow=self.flow_obj, created_on=started, is_active=True).order_by("-modified_on").first()) if not run or run.submitted_by != self.submitted_by_obj: run = FlowRun.create(self.flow_obj, self.contact_obj, created_on=started, submitted_by=self.submitted_by_obj) run.update_from_surveyor(steps) if completed: completed_on = steps[len(steps) - 1]["arrived_on"] if steps else None run.set_completed(exit_uuid=None, completed_on=completed_on) else: run.save(update_fields=("modified_on", )) return run
def validate_extra(self, value): # request is parsed by DRF.JSONParser, and if extra is a valid json it gets deserialized as dict # in any other case we need to raise a ValidationError if not isinstance(value, dict): raise serializers.ValidationError("Must be a valid JSON value") if not value: # pragma: needs cover return None else: return FlowRun.normalize_fields(value)[0]
def _create_runs(self, count, flow, contacts): """ Creates the given number of flow runs """ runs = [] for c in range(0, count): contact = contacts[c % len(contacts)] runs.append(FlowRun.create(flow, contact, db_insert=False)) FlowRun.objects.bulk_create(runs) return runs
def remove_never_expirations(apps, schema_editor): from temba.flows.models import Flow, FlowRun # for every flow that has an expiration set to 0 for flow in Flow.objects.filter(expires_after_minutes=0): # set the expiration to 30 days flow.expires_after_minutes = 60 * 24 * 30 flow.save(update_fields=["expires_after_minutes"]) # recalculate expiration on any active runs (possibly expiring them) for run in FlowRun.objects.filter(flow=flow, is_active=True): run.flow = flow if run.path: last_arrived_on = iso8601.parse_date(run.path[-1]["arrived_on"]) run.update_expiration(last_arrived_on) # check and expire any flows that are now expired runs = FlowRun.objects.filter(is_active=True, expires_on__lte=timezone.now()).order_by("expires_on") FlowRun.bulk_exit(runs, FlowRun.EXIT_TYPE_EXPIRED)
def exit_active_flowruns(Contact, log=False): from temba.flows.models import FlowRun exit_runs = [] # find all contacts that have more than one active run active_contact_ids = Contact.objects.filter(runs__is_active=True).order_by('id')\ .annotate(run_count=Count('id')).filter(run_count__gt=1).values_list('id', flat=True) if log: print "%d contacts to evaluate runs for" % len(active_contact_ids) for idx, contact_id in enumerate(active_contact_ids): active_runs = FlowRun.objects.filter( contact_id=contact_id, is_active=True).order_by('-modified_on') # more than one? we may need to expire some if len(active_runs) > 1: last = active_runs[0] contact_exit_runs = [r.id for r in active_runs[1:]] ancestor = last.parent while ancestor: exit_runs.remove(ancestor.id) ancestor = ancestor.parent exit_runs += contact_exit_runs if (idx % 100) == 0: if log: print " - %d / %d contacts evaluated. %d runs to exit" % ( idx, len(active_contact_ids), len(exit_runs)) # ok, now exit those runs exited = 0 for batch in chunk_list(exit_runs, 1000): runs = FlowRun.objects.filter(id__in=batch) FlowRun.bulk_exit(runs, FlowRun.EXIT_TYPE_INTERRUPTED, timezone.now()) exited += len(batch) if log: print " * %d / %d runs exited." % (exited, len(exit_runs))
def save(self): user = self.context['user'] contacts = self.validated_data['contacts'] action = self.validated_data['action'] group = self.validated_data.get('group') if action == self.ADD: group.update_contacts(user, contacts, add=True) elif action == self.REMOVE: group.update_contacts(user, contacts, add=False) elif action == self.INTERRUPT: FlowRun.exit_all_for_contacts(contacts, FlowRun.EXIT_TYPE_INTERRUPTED) elif action == self.ARCHIVE: Msg.archive_all_for_contacts(contacts) else: for contact in contacts: if action == self.BLOCK: contact.block(user) elif action == self.UNBLOCK: contact.unblock(user) elif action == self.DELETE: contact.release(user)
def exit_active_flowruns(Contact, log=False): from temba.flows.models import FlowRun exit_runs = [] # find all contacts that have more than one active run active_contact_ids = Contact.objects.filter(runs__is_active=True).order_by('id')\ .annotate(run_count=Count('id')).filter(run_count__gt=1).values_list('id', flat=True) if log: print "%d contacts to evaluate runs for" % len(active_contact_ids) for idx, contact_id in enumerate(active_contact_ids): active_runs = FlowRun.objects.filter(contact_id=contact_id, is_active=True).order_by('-modified_on') # more than one? we may need to expire some if len(active_runs) > 1: last = active_runs[0] contact_exit_runs = [r.id for r in active_runs[1:]] ancestor = last.parent while ancestor: exit_runs.remove(ancestor.id) ancestor = ancestor.parent exit_runs += contact_exit_runs if (idx % 100) == 0: if log: print " - %d / %d contacts evaluated. %d runs to exit" % (idx, len(active_contact_ids), len(exit_runs)) # ok, now exit those runs exited = 0 for batch in chunk_list(exit_runs, 1000): runs = FlowRun.objects.filter(id__in=batch) FlowRun.bulk_exit(runs, FlowRun.EXIT_TYPE_INTERRUPTED, timezone.now()) exited += len(batch) if log: print " * %d / %d runs exited." % (exited, len(exit_runs))
def enable_flow_server(org): """ Enables the flow server for an org. This switches all flows to be flow-server-enabled and switched all handling to take place through Mailroom going forward. Note that people currently in flows will be interrupted and there's no going back after doing this. """ from temba.flows.models import FlowRun # update all channels (we do this first as this may throw and we don't want to do the rest unless it succeeds) for channel in org.channels.filter(is_active=True): channel_type = channel.get_type() channel_type.enable_flow_server(channel) # interrupt all active runs FlowRun.bulk_exit(org.runs.filter(is_active=True), FlowRun.EXIT_TYPE_INTERRUPTED) # flip all flows org.flows.filter(is_active=True).update(flow_server_enabled=True) # finally flip our org org.flow_server_enabled = True org.save(update_fields=["flow_server_enabled", "modified_on"])
def _create_runs(self, count, flow, contacts): """ Creates the given number of flow runs """ runs = [] for c in range(0, count): contact = contacts[c % len(contacts)] runs.append(FlowRun.create(flow, contact.pk, db_insert=False)) FlowRun.objects.bulk_create(runs) # add a step to each run steps = [] for run in FlowRun.objects.all(): steps.append(FlowStep(run=run, contact=run.contact, step_type='R', step_uuid=flow.entry_uuid, arrived_on=timezone.now())) FlowStep.objects.bulk_create(steps) return runs
def _create_runs(self, count, flow, contacts): """ Creates the given number of flow runs """ runs = [] for c in range(0, count): contact = contacts[c % len(contacts)] runs.append(FlowRun.create(flow, contact.pk, db_insert=False)) FlowRun.objects.bulk_create(runs) # add a step to each run steps = [] for run in FlowRun.objects.all(): steps.append( FlowStep(run=run, contact=run.contact, step_type='R', step_uuid=flow.entry_uuid, arrived_on=timezone.now())) FlowStep.objects.bulk_create(steps) return runs
def get(self, request, *args, **kwargs): from temba.flows.models import FlowSession from temba.ivr.models import IVRCall action = kwargs["action"].lower() request_body = force_text(request.body) request_path = request.get_full_path() request_method = request.method request_uuid = kwargs["uuid"] if action == "event": if not request_body: return HttpResponse("") body_json = json.loads(request_body) status = body_json.get("status", None) duration = body_json.get("duration", None) call_uuid = body_json.get("uuid", None) conversation_uuid = body_json.get("conversation_uuid", None) if call_uuid is None: return HttpResponse("Missing uuid parameter, ignoring") call = IVRCall.objects.filter(external_id=call_uuid).first() if not call: # try looking up by the conversation uuid (inbound calls start with that) call = IVRCall.objects.filter( external_id=conversation_uuid).first() if call: call.external_id = call_uuid call.save() else: response = dict(message="Call not found for %s" % call_uuid) return JsonResponse(response) channel = call.channel channel_type = channel.channel_type call.update_status(status, duration, channel_type) call.save() response = dict(description="Updated call status", call=dict(status=call.get_status_display(), duration=call.duration)) event = HttpEvent(request_method, request_path, request_body, 200, json.dumps(response)) ChannelLog.log_ivr_interaction(call, "Updated call status", event) if call.status == IVRCall.COMPLETED: # if our call is completed, hangup runs = FlowRun.objects.filter(connection=call) for run in runs: if not run.is_completed(): run.set_completed(exit_uuid=None) return JsonResponse(response) if action == "answer": if not request_body: return HttpResponse("") body_json = json.loads(request_body) from_number = body_json.get("from", None) channel_number = body_json.get("to", None) external_id = body_json.get("conversation_uuid", None) if not from_number or not channel_number or not external_id: return HttpResponse("Missing parameters, Ignoring") # look up the channel address_q = Q(address=channel_number) | Q(address=("+" + channel_number)) channel = Channel.objects.filter(address_q).filter( is_active=True, channel_type="NX").first() # make sure we got one, and that it matches the key for our org org_uuid = None if channel: org_uuid = channel.org.config.get(NEXMO_UUID, None) if not channel or org_uuid != request_uuid: return HttpResponse("Channel not found for number: %s" % channel_number, status=404) urn = URN.from_tel(from_number) contact, urn_obj = Contact.get_or_create(channel.org, urn, channel) flow = Trigger.find_flow_for_inbound_call(contact) if flow: call = IVRCall.create_incoming(channel, contact, urn_obj, channel.created_by, external_id) session = FlowSession.create(contact, connection=call) FlowRun.create(flow, contact, session=session, connection=call) response = Flow.handle_call(call) channel_type = channel.channel_type call.update_status("answered", None, channel_type) event = HttpEvent(request_method, request_path, request_body, 200, str(response)) ChannelLog.log_ivr_interaction(call, "Incoming request for call", event) return JsonResponse(json.loads(str(response)), safe=False) else: # we don't have an inbound trigger to deal with this call. response = channel.generate_ivr_response() # say nothing and hangup, this is a little rude, but if we reject the call, then # they'll get a non-working number error. We send 'busy' when our server is down # so we don't want to use that here either. response.say("") response.hangup() # if they have a missed call trigger, fire that off Trigger.catch_triggers(contact, Trigger.TYPE_MISSED_CALL, channel) # either way, we need to hangup now return JsonResponse(json.loads(str(response)), safe=False)
def post(self, request, *args, **kwargs): from twilio.request_validator import RequestValidator from temba.flows.models import FlowSession signature = request.META.get("HTTP_X_TWILIO_SIGNATURE", "") url = "https://" + request.get_host() + "%s" % request.get_full_path() channel_uuid = kwargs.get("uuid") call_sid = self.get_param("CallSid") direction = self.get_param("Direction") status = self.get_param("CallStatus") to_number = self.get_param("To") to_country = self.get_param("ToCountry") from_number = self.get_param("From") # Twilio sometimes sends un-normalized numbers if to_number and not to_number.startswith("+") and to_country: # pragma: no cover to_number, valid = URN.normalize_number(to_number, to_country) # see if it's a twilio call being initiated if to_number and call_sid and direction == "inbound" and status == "ringing": # find a channel that knows how to answer twilio calls channel = self.get_ringing_channel(uuid=channel_uuid) if not channel: response = VoiceResponse() response.say("Sorry, there is no channel configured to take this call. Goodbye.") response.hangup() return HttpResponse(str(response)) org = channel.org if self.get_channel_type() == "T" and not org.is_connected_to_twilio(): return HttpResponse("No Twilio account is connected", status=400) client = self.get_client(channel=channel) validator = RequestValidator(client.auth[1]) signature = request.META.get("HTTP_X_TWILIO_SIGNATURE", "") url = "https://%s%s" % (request.get_host(), request.get_full_path()) if validator.validate(url, request.POST, signature): from temba.ivr.models import IVRCall # find a contact for the one initiating us urn = URN.from_tel(from_number) contact, urn_obj = Contact.get_or_create(channel.org, urn, channel) flow = Trigger.find_flow_for_inbound_call(contact) if flow: call = IVRCall.create_incoming(channel, contact, urn_obj, channel.created_by, call_sid) session = FlowSession.create(contact, connection=call) call.update_status( request.POST.get("CallStatus", None), request.POST.get("CallDuration", None), "T" ) call.save() FlowRun.create(flow, contact, session=session, connection=call) response = Flow.handle_call(call) return HttpResponse(str(response)) else: # we don't have an inbound trigger to deal with this call. response = channel.generate_ivr_response() # say nothing and hangup, this is a little rude, but if we reject the call, then # they'll get a non-working number error. We send 'busy' when our server is down # so we don't want to use that here either. response.say("") response.hangup() # if they have a missed call trigger, fire that off Trigger.catch_triggers(contact, Trigger.TYPE_MISSED_CALL, channel) # either way, we need to hangup now return HttpResponse(str(response)) # check for call progress events, these include post-call hangup notifications if request.POST.get("CallbackSource", None) == "call-progress-events": if call_sid: from temba.ivr.models import IVRCall call = IVRCall.objects.filter(external_id=call_sid).first() if call: call.update_status( request.POST.get("CallStatus", None), request.POST.get("CallDuration", None), "TW" ) call.save() return HttpResponse("Call status updated") return HttpResponse("No call found") return HttpResponse("Not Handled, unknown action", status=400) # pragma: no cover
def validate_extra(self, value): if not value: # pragma: needs cover return None else: return FlowRun.normalize_fields(value)[0]
def get(self, request, *args, **kwargs): from temba.flows.models import FlowSession from temba.ivr.models import IVRCall action = kwargs["action"].lower() request_body = force_text(request.body) request_path = request.get_full_path() request_method = request.method request_uuid = kwargs["uuid"] if action == "event": if not request_body: return HttpResponse("") body_json = json.loads(request_body) status = body_json.get("status", None) duration = body_json.get("duration", None) call_uuid = body_json.get("uuid", None) conversation_uuid = body_json.get("conversation_uuid", None) if call_uuid is None: return HttpResponse("Missing uuid parameter, ignoring") call = IVRCall.objects.filter(external_id=call_uuid).first() if not call: # try looking up by the conversation uuid (inbound calls start with that) call = IVRCall.objects.filter(external_id=conversation_uuid).first() if call: call.external_id = call_uuid call.save() else: response = dict(message="Call not found for %s" % call_uuid) return JsonResponse(response) channel = call.channel channel_type = channel.channel_type call.update_status(status, duration, channel_type) call.save() response = dict( description="Updated call status", call=dict(status=call.get_status_display(), duration=call.duration) ) event = HttpEvent(request_method, request_path, request_body, 200, json.dumps(response)) ChannelLog.log_ivr_interaction(call, "Updated call status", event) if call.status == IVRCall.COMPLETED: # if our call is completed, hangup runs = FlowRun.objects.filter(connection=call) for run in runs: if not run.is_completed(): run.set_completed(exit_uuid=None) return JsonResponse(response) if action == "answer": if not request_body: return HttpResponse("") body_json = json.loads(request_body) from_number = body_json.get("from", None) channel_number = body_json.get("to", None) external_id = body_json.get("conversation_uuid", None) if not from_number or not channel_number or not external_id: return HttpResponse("Missing parameters, Ignoring") # look up the channel address_q = Q(address=channel_number) | Q(address=("+" + channel_number)) channel = Channel.objects.filter(address_q).filter(is_active=True, channel_type="NX").first() # make sure we got one, and that it matches the key for our org org_uuid = None if channel: org_uuid = channel.org.config.get(NEXMO_UUID, None) if not channel or org_uuid != request_uuid: return HttpResponse("Channel not found for number: %s" % channel_number, status=404) urn = URN.from_tel(from_number) contact, urn_obj = Contact.get_or_create(channel.org, urn, channel) flow = Trigger.find_flow_for_inbound_call(contact) if flow: call = IVRCall.create_incoming(channel, contact, urn_obj, channel.created_by, external_id) session = FlowSession.create(contact, connection=call) FlowRun.create(flow, contact, session=session, connection=call) response = Flow.handle_call(call) channel_type = channel.channel_type call.update_status("answered", None, channel_type) event = HttpEvent(request_method, request_path, request_body, 200, str(response)) ChannelLog.log_ivr_interaction(call, "Incoming request for call", event) return JsonResponse(json.loads(str(response)), safe=False) else: # we don't have an inbound trigger to deal with this call. response = channel.generate_ivr_response() # say nothing and hangup, this is a little rude, but if we reject the call, then # they'll get a non-working number error. We send 'busy' when our server is down # so we don't want to use that here either. response.say("") response.hangup() # if they have a missed call trigger, fire that off Trigger.catch_triggers(contact, Trigger.TYPE_MISSED_CALL, channel) # either way, we need to hangup now return JsonResponse(json.loads(str(response)), safe=False)
def validate_extra(self, value): if not value: return None else: return FlowRun.normalize_fields(value)[0]
def post(self, request, *args, **kwargs): from twilio.request_validator import RequestValidator from temba.flows.models import FlowSession signature = request.META.get("HTTP_X_TWILIO_SIGNATURE", "") url = "https://" + request.get_host() + "%s" % request.get_full_path() channel_uuid = kwargs.get("uuid") call_sid = self.get_param("CallSid") direction = self.get_param("Direction") status = self.get_param("CallStatus") to_number = self.get_param("To") to_country = self.get_param("ToCountry") from_number = self.get_param("From") # Twilio sometimes sends un-normalized numbers if to_number and not to_number.startswith( "+") and to_country: # pragma: no cover to_number, valid = URN.normalize_number(to_number, to_country) # see if it's a twilio call being initiated if to_number and call_sid and direction == "inbound" and status == "ringing": # find a channel that knows how to answer twilio calls channel = self.get_ringing_channel(uuid=channel_uuid) if not channel: response = VoiceResponse() response.say( "Sorry, there is no channel configured to take this call. Goodbye." ) response.hangup() return HttpResponse(str(response)) org = channel.org if self.get_channel_type( ) == "T" and not org.is_connected_to_twilio(): return HttpResponse("No Twilio account is connected", status=400) client = self.get_client(channel=channel) validator = RequestValidator(client.auth[1]) signature = request.META.get("HTTP_X_TWILIO_SIGNATURE", "") url = "https://%s%s" % (request.get_host(), request.get_full_path()) if validator.validate(url, request.POST, signature): from temba.ivr.models import IVRCall # find a contact for the one initiating us urn = URN.from_tel(from_number) contact, urn_obj = Contact.get_or_create( channel.org, urn, channel) flow = Trigger.find_flow_for_inbound_call(contact) if flow: call = IVRCall.create_incoming(channel, contact, urn_obj, channel.created_by, call_sid) session = FlowSession.create(contact, connection=call) call.update_status(request.POST.get("CallStatus", None), request.POST.get("CallDuration", None), "T") call.save() FlowRun.create(flow, contact, session=session, connection=call) response = Flow.handle_call(call) return HttpResponse(str(response)) else: # we don't have an inbound trigger to deal with this call. response = channel.generate_ivr_response() # say nothing and hangup, this is a little rude, but if we reject the call, then # they'll get a non-working number error. We send 'busy' when our server is down # so we don't want to use that here either. response.say("") response.hangup() # if they have a missed call trigger, fire that off Trigger.catch_triggers(contact, Trigger.TYPE_MISSED_CALL, channel) # either way, we need to hangup now return HttpResponse(str(response)) # check for call progress events, these include post-call hangup notifications if request.POST.get("CallbackSource", None) == "call-progress-events": if call_sid: from temba.ivr.models import IVRCall call = IVRCall.objects.filter(external_id=call_sid).first() if call: call.update_status(request.POST.get("CallStatus", None), request.POST.get("CallDuration", None), "TW") call.save() return HttpResponse("Call status updated") return HttpResponse("No call found") return HttpResponse("Not Handled, unknown action", status=400) # pragma: no cover
def test_runs_offset(self): url = reverse('api.v2.runs') self.assertEndpointAccess(url) flow1 = self.create_flow(uuid_start=0) for i in range(600): FlowRun.create(flow1, self.joe.pk) with patch.object(timezone, 'now', return_value=datetime(2015, 9, 15, 0, 0, 0, 0, pytz.UTC)): now = timezone.now() for r in FlowRun.objects.all(): r.modified_on = now r.save() with self.settings(CURSOR_PAGINATION_OFFSET_CUTOFF=10): response = self.fetchJSON(url) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) query = response.json['next'].split('?')[1] response = self.fetchJSON(url, query=query) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) query = response.json['next'].split('?')[1] response = self.fetchJSON(url, query=query) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) query = response.json['next'].split('?')[1] response = self.fetchJSON(url, query=query) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) with self.settings(CURSOR_PAGINATION_OFFSET_CUTOFF=400): url = reverse('api.v2.runs') response = self.fetchJSON(url) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) query = response.json['next'].split('?')[1] response = self.fetchJSON(url, query=query) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) query = response.json['next'].split('?')[1] response = self.fetchJSON(url, query=query) self.assertEqual(len(response.json['results']), 200) self.assertIsNone(response.json['next']) with self.settings(CURSOR_PAGINATION_OFFSET_CUTOFF=5000): url = reverse('api.v2.runs') response = self.fetchJSON(url) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) query = response.json['next'].split('?')[1] response = self.fetchJSON(url, query=query) self.assertEqual(len(response.json['results']), 250) self.assertTrue(response.json['next']) query = response.json['next'].split('?')[1] response = self.fetchJSON(url, query=query) self.assertEqual(len(response.json['results']), 100) self.assertIsNone(response.json['next'])
def remove_expired_flows_from_active(apps, schema_editor): r = get_redis_connection() for key in r.keys('*:step_active_set:*'): # make sure our flow run activity is removed FlowRun.do_expire_runs(FlowRun.objects.filter(pk__in=r.smembers(key), is_active=False, contact__is_test=False))