Skip to content
This repository was archived by the owner on Jul 15, 2021. It is now read-only.

TurboVote

Aaron Schachter edited this page Apr 4, 2019 · 1 revision

Context


Users can register to vote on a third-party platform called TurboVote. We send our members to that site to register and in the link to the site we send some specifically formatted information like NSID, campaign ID, source, etc.

Here’s an example for an email send: https://testing-dosomething.turbovote.org/?r=user:{northstarID},campaignID:{campaignID},campaignRunID:{legacyRunID},source:email,source_details:newsletter_{newsletterID}

TurboVote then provides us a CSV of individual records that reflects what happened on their platform. Here's an example of the some of the columns available in that CSV -- columns with personal information have been left out and NS ID has been hidden! The highlighted pink rows will be addressed in the hierarchy section below.

We want to import this CSV into Rogue as signups and posts so that these users can be served experiences based on their registration status, they can be messaged, and we can accurately report this information in Looker (and do deeper data analysis when needed). 


Problem


 TurboVote records and Rogue posts do not have the same schema! We want to figure out how to store TV records in Rogue so that other DoSomething Apps can get this information via the Rogue API and be able to serve this data to internal teams and serve customized user experiences based on a user’s registration status.

So, how should TV records be imported into Rogue?

Solution

Admin Workflowimage uploaded from ios 8

Record cleaning 💅

There are some records that might show up in the TV record that we want to ignore. The rules around that data cleaning are outlined below:

  • If an email includes thing.org in the address, ignore it.
  • If a hostname includes `testing, ignore it.
  • If an email includes @dosome in the address, ignore it.
  • If a last name includes Baloney, ignore it.
  • If an email includes turbovote, ignore it.

Status Translation Rules

Ultimately, there are 5 new post statuses we want to capture for voter-reg posts:

  • register-form - User completed the registration form
  • register-OVR - User completed the registration form on their state's Online Voter Registration platform
  • confirmed - User just confirmed that they are registered (self-reported)
  • ineligible - User is ineligible to register for whatever reason
  • uncertain - We can not be certain about this user registration status

This is the logic for how to determine what a post status should be.

        switch($tvStatus)
        {
            case 'initiated':
                $translatedStatus = 'register-form';
                break;
            case 'registered':
                $translatedStatus = $tvMethod === 'online' ? 'register-OVR' : 'confirmed';
                break;
            case 'unknown':
            case 'pending':
                $translatedStatus = 'uncertain';
                break;
            case 'ineligible':
            case 'not-required':
                $translatedStatus = 'ineligible';
                break;
            default:
                $translatedStatus = 'pending';
        }

Status Hierarchy

When a TV CSV has multiple records per user we use the following hierarchy to determine which status should be reported on the Rogue post

  1. register-form
  2. register-OVR
  3. confirmed
  4. ineligible
  5. uncertain

We’ve established this hierarchy because each time a user interacts with the TurboVote form a new row is created in the CSV. In the above screenshot, the highlighted pink cells are an example of the same user interacting with the form more than once. The hierarchy is the simplest approach to dealing with varying statuses, but we anticipate some edge cases that we may need to deal with as they come up.

Here’s one example: 


  • User A completes the TurboVote form —> register-form status
  • User A, for whatever reason, starts the TurboVote form again and drops off --> uncertain status

In this case, we would want to count the form completion (register-form). It’s important to note that the hierarchy is for internal reporting and doesn’t prevent the user from interacting with the TurboVote form if they want to do so.

How to count these as impact

Based on the above 5 statuses, some should be counted as a RB and some should not.** This determination was made by the executive team and allows us to report internally progress towards the organization's report back goal. Here's what counts as a reportback:

  • register-form
  • register-OVR
  • confirmed

Note: register-form and register-OVR are the only statuses that count as registrations.

Rogue will NOT store this information, but will return a derived value in the JSON response when the voter registration post is created or read that holds this information. The logic to determine this is as follows: 


if (in_array($rogueStatus, ['confirmed', 'register-form', 'register-OVR'])) {
    $reportbackStatus = 'T';
} else {
    $reportbackStatus = 'F';
}

Other Important Information

  • The submission_created_at date is when the importer ran. Details about when the registration was created/updated are in the source_details.
  • All of these signups will have a source of importer-client (this is how messaging is suppressed in C.io)
  • All of these posts have a type = voter-reg
  • The month that the registration came in is what informs the action column (e.g., february-2018-turbovote)

How this Might Impact Other Teams

Team Storm

  • Will need to be able to accept JSON with new derived reportback value
  • Looks will need to be updated

Team O'Doyle

  • To know if a user is registered or not, updates will need to be made so that (Blink, Gambit, C.io) to accept new statuses (e.g., register-form). This includes any validation on the status field.
  • To know if a user has reported back, updates will need to be made (Blink, Gambit, C.io) to read the derived value returned in the JSON response and use it to act upon.

Team Rocket

  • To know if a user is registered or not, updates will need to be made (Phoenix-next) to read the new statuses of a voter-reg post (e.g., register-form)
  • To know if a user has reported back, updates will need to be made (Phoenix-next) to read from the derived report back value

Team Bleed

  • Build out this logic in the importer app
  • Update validation on post status column to accept more statuses
  • Test that voter reg posts are importing correctly and being sent to other systems

GOTCHAS

  • There is a 1:1 relationship between a user (unique NSID) and a post.
  • For now, we will attribute everyvoter-reg post to the Grab the Mic campaign to maintain a 1:1 relationship
  • Quasar will be pushed these posts from Rogue but if Data needs to do a deeper analysis, they will use the raw TV CSV to do that work.
  • If a user shares their UTM'ed URL with other people, there could be duplicate referral codes but associated with different registrants: See a screenshot of what this data looks like (note: the user depicted in this spreadsheet is fake.)
  • The post created_at date will be when we imported the CSV; the actual registration created_at will be in the source_details.

Open Questions

  • Should final registration status live on a users NS profile? If so should rogue be involved at all.
  • We'll need to build the logic to create NS IDs for non-members - should we only create these accounts for the statuses we care about (register-form and register-OVR)?

Clone this wiki locally