def pollrun_start(pollrun_id): """ Starts a newly created pollrun by creating runs in RapidPro and creating empty responses for them. """ from tracpro.polls.models import PollRun, Response pollrun = PollRun.objects.select_related('poll', 'region').get(pk=pollrun_id) if pollrun.pollrun_type not in (PollRun.TYPE_PROPAGATED, PollRun.TYPE_REGIONAL): raise ValueError("Can't start non-regional poll") org = pollrun.poll.org client = org.get_temba_client() contacts = Contact.objects.filter(is_active=True) if pollrun.pollrun_type == PollRun.TYPE_PROPAGATED: descendants = pollrun.region.get_descendants(include_self=True) contacts = contacts.filter(region__in=descendants) elif pollrun.pollrun_type == PollRun.TYPE_REGIONAL: contacts = contacts.filter(region=pollrun.region) contact_uuids = contacts.values_list('uuid', flat=True) runs = client.create_runs(pollrun.poll.flow_uuid, contact_uuids, restart_participants=True) for run in runs: Response.create_empty(org, pollrun, run) logger.info("Created %d new runs for new poll pollrun #%d" % (len(runs), pollrun.pk))
def test_list(self): pollrun1 = factories.UniversalPollRun( poll=self.poll1, conducted_on=datetime.datetime(2014, 12, 1, tzinfo=pytz.UTC)) Response.create_empty( self.unicef, pollrun1, Run.create(id=123, contact='C-001', created_on=timezone.now())) self.login(self.admin) response = self.url_get('unicef', reverse('contacts.contact_list')) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context['object_list']), 5) # no poll pollruns shown in "All Regions" view self.assertNotContains(response, "Farm Poll") url = '{}?search=an'.format(reverse('contacts.contact_list')) response = self.url_get('unicef', url) self.assertEqual(len(response.context['object_list']), 2) self.assertContains(response, "Ann") self.assertContains(response, "Dan") self.login(self.user1) response = self.url_get('unicef', reverse('contacts.contact_list')) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context['object_list']), 3) self.assertContains(response, "Farm Poll")
def pollrun_start(pollrun_id): """ Starts a newly created pollrun by creating runs in RapidPro and creating empty responses for them. """ from tracpro.polls.models import PollRun, Response pollrun = PollRun.objects.select_related('poll', 'region').get(pk=pollrun_id) if pollrun.pollrun_type not in (PollRun.TYPE_PROPAGATED, PollRun.TYPE_REGIONAL): raise ValueError("Can't start non-regional poll") org = pollrun.poll.org client = org.get_temba_client() contacts = Contact.objects.active() if pollrun.pollrun_type == PollRun.TYPE_PROPAGATED: descendants = pollrun.region.get_descendants(include_self=True) contacts = contacts.filter(region__in=descendants) elif pollrun.pollrun_type == PollRun.TYPE_REGIONAL: contacts = contacts.filter(region=pollrun.region) contact_uuids = list(contacts.values_list('uuid', flat=True)) runs = client.create_runs(pollrun.poll.flow_uuid, contact_uuids, restart_participants=True) for run in runs: Response.create_empty(org, pollrun, run) logger.info("Created %d new runs for new poll pollrun #%d" % (len(runs), pollrun.pk))
def pollrun_restart_participants(pollrun_id, contact_uuids): """ Restarts the given contacts in the given poll pollrun by replacing any existing response they have with an empty one. """ from tracpro.polls.models import PollRun, Response pollrun = PollRun.objects.select_related('poll', 'region').get(pk=pollrun_id) if pollrun.pollrun_type not in (PollRun.TYPE_REGIONAL, PollRun.TYPE_PROPAGATED): raise ValueError("Can't restart participants of a non-regional poll") if not pollrun.is_last_for_region(pollrun.region): raise ValueError("Can only restart last pollrun of poll for a region") org = pollrun.poll.org client = org.get_temba_client() runs = client.create_runs(pollrun.poll.flow_uuid, contact_uuids, restart_participants=True) for run in runs: Response.create_empty(org, pollrun, run) logger.info("Created %d restart runs for poll pollrun #%d" % (len(runs), pollrun.pk))
def org_task(self, org): """ Fetches new and modified flow runs for the given org and creates/updates poll responses. """ from tracpro.orgs_ext.constants import TaskType from tracpro.polls.models import Poll, PollRun, Response client = org.get_temba_client() redis_connection = get_redis_connection() last_time_key = LAST_FETCHED_RUN_TIME_KEY % org.pk last_time = redis_connection.get(last_time_key) if last_time is not None: last_time = parse_iso8601(last_time) else: newest_runs = Response.objects.filter( pollrun__poll__org=org).order_by('-created_on') newest_runs = newest_runs.exclude( pollrun__pollrun_type=PollRun.TYPE_SPOOFED) newest_run = newest_runs.first() last_time = newest_run.created_on if newest_run else None until = timezone.now() total_runs = 0 for poll in Poll.objects.active().by_org(org): poll_runs = client.get_runs(flows=[poll.flow_uuid], after=last_time, before=until) total_runs += len(poll_runs) # convert flow runs into poll responses for run in poll_runs: try: Response.from_run(org, run, poll=poll) except ValueError as e: logger.error("Unable to save run #%d due to error: %s" % (run.id, e.message)) continue logger.info("Fetched %d new and updated runs for org #%d (since=%s)" % (total_runs, org.id, format_iso8601(last_time) if last_time else 'Never')) task_result = dict(time=datetime_to_ms(timezone.now()), counts=dict(fetched=total_runs)) org.set_task_result(TaskType.fetch_runs, task_result) redis_connection.set(last_time_key, format_iso8601(until))
def fetch_org_runs(org_id): """ Fetches new and modified flow runs for the given org and creates/updates poll responses. """ from tracpro.orgs_ext.constants import TaskType from tracpro.polls.models import Poll, PollRun, Response org = Org.objects.get(pk=org_id) client = org.get_temba_client() redis_connection = get_redis_connection() last_time_key = LAST_FETCHED_RUN_TIME_KEY % org.pk last_time = redis_connection.get(last_time_key) if last_time is not None: last_time = parse_iso8601(last_time) else: newest_runs = Response.objects.filter(pollrun__poll__org=org).order_by("-created_on") newest_runs = newest_runs.exclude(pollrun__pollrun_type=PollRun.TYPE_SPOOFED) newest_run = newest_runs.first() last_time = newest_run.created_on if newest_run else None until = timezone.now() total_runs = 0 for poll in Poll.get_all(org): poll_runs = client.get_runs(flows=[poll.flow_uuid], after=last_time, before=until) total_runs += len(poll_runs) # convert flow runs into poll responses for run in poll_runs: try: Response.from_run(org, run, poll=poll) except ValueError as e: logger.error("Unable to save run #%d due to error: %s" % (run.id, e.message)) continue logger.info( "Fetched %d new and updated runs for org #%d (since=%s)" % (total_runs, org.id, format_iso8601(last_time) if last_time else "Never") ) task_result = dict(time=datetime_to_ms(timezone.now()), counts=dict(fetched=total_runs)) org.set_task_result(TaskType.fetch_runs, task_result) redis_connection.set(last_time_key, format_iso8601(until))
def handle(self, *args, **options): org_id = int(args[0]) if args else None if not org_id: raise CommandError("Most provide valid org id") try: org = Org.objects.get(pk=org_id) except Org.DoesNotExist: raise CommandError("No such org with id %d" % org_id) minutes, hours, days = options['minutes'], options['hours'], options[ 'days'] if not (minutes or hours or days): raise CommandError( "Must provide at least one of --minutes --hours or --days") since = timezone.now() - relativedelta( minutes=minutes, hours=hours, days=days) self.stdout.write('Fetching responses for org %s since %s...' % (org.name, since.strftime('%b %d, %Y %H:%M'))) client = org.get_temba_client() polls_by_flow_uuids = { p.flow_uuid: p for p in Poll.objects.active().by_org(org) } runs = client.get_runs(flows=polls_by_flow_uuids.keys(), after=since) self.stdout.write("Fetched %d runs for org %s" % (len(runs), org.id)) created = 0 updated = 0 for run in runs: if run.flow not in polls_by_flow_uuids: continue # Response is for a Poll not tracked for this org. poll = polls_by_flow_uuids[run.flow] try: response = Response.from_run(org, run, poll=poll) except ValueError as e: self.stderr.write("Unable to save run #%d due to error: %s" % (run.id, e.message)) continue if getattr(response, 'is_new', False): created += 1 else: updated += 1 self.stdout.write( "Created %d new responses and updated %d existing responses" % (created, updated))
def test_list(self): issue1 = Issue.objects.create(poll=self.poll1, region=None, conducted_on=self.datetime(2014, 12, 1)) Response.create_empty(self.unicef, issue1, Run.create(id=123, contact='C-001', created_on=timezone.now())) self.login(self.admin) response = self.url_get('unicef', reverse('contacts.contact_list')) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context['object_list']), 5) self.assertNotContains(response, "Farm Poll") # no poll issues shown in "All Regions" view response = self.url_get('unicef', '%s?search=an' % reverse('contacts.contact_list')) self.assertEqual(len(response.context['object_list']), 2) self.assertContains(response, "Ann") self.assertContains(response, "Dan") self.login(self.user1) response = self.url_get('unicef', reverse('contacts.contact_list')) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context['object_list']), 3) self.assertContains(response, "Farm Poll")
def handle(self, *args, **options): org_id = int(args[0]) if args else None if not org_id: raise CommandError("Most provide valid org id") try: org = Org.objects.get(pk=org_id) except Org.DoesNotExist: raise CommandError("No such org with id %d" % org_id) minutes, hours, days = options['minutes'], options['hours'], options['days'] if not (minutes or hours or days): raise CommandError("Must provide at least one of --minutes --hours or --days") since = timezone.now() - relativedelta(minutes=minutes, hours=hours, days=days) self.stdout.write('Fetching responses for org %s since %s...' % (org.name, since.strftime('%b %d, %Y %H:%M'))) client = org.get_temba_client() polls_by_flow_uuids = {p.flow_uuid: p for p in Poll.get_all(org)} runs = client.get_runs(flows=polls_by_flow_uuids.keys(), after=since) self.stdout.write("Fetched %d runs for org %s" % (len(runs), org.id)) created = 0 updated = 0 for run in runs: if run.flow not in polls_by_flow_uuids: continue # Response is for a Poll not tracked for this org. poll = polls_by_flow_uuids[run.flow] try: response = Response.from_run(org, run, poll=poll) except ValueError as e: self.stderr.write("Unable to save run #%d due to error: %s" % (run.id, e.message)) continue if getattr(response, 'is_new', False): created += 1 else: updated += 1 self.stdout.write("Created %d new responses and updated %d existing responses" % (created, updated))