{"action":"created","comment":{"url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/comments/181195447","pull_request_review_id":111759795,"id":181195447,"diff_hunk":"@@ -0,0 +1,239 @@\n+# Recidiviz - a platform for tracking granular recidivism metrics in real time\n+# Copyright (C) 2018 Recidiviz, Inc.\n+#\n+# This program is free software: you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation, either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+# This program is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with this program.  If not, see <https://www.gnu.org/licenses/>.\n+# =============================================================================\n+\n+\n+import logging\n+\n+from google.appengine.ext import deferred\n+from google.appengine.ext import ndb\n+from us_ny.us_ny_inmate_snapshot import UsNyInmateSnapshot\n+from us_ny.us_ny_inmate import UsNyInmate\n+from us_ny.us_ny_record import UsNyRecord\n+from models.inmate_snapshot import InmateFacilitySnapshot\n+import webapp2\n+\n+\n+TEST_BATCH_SIZE = 10\n+\n+class DataMigration(webapp2.RequestHandler):\n+\n+    def get(self):\n+        \"\"\"\n+        get()\n+        Request handler for data migration tasks. Migrates last_release_date \n+        and last_release_type from UsNyRecord model to Record model, and old\n+        InmateFacilitySnapshot entities to new InmateSnapshot entities.\n+\n+        Example queries:\n+        # Migrate 5x snapshots only.\n+        http://localhost:8080/data_migration?rand_var=898230981029381274897247&test_only=true&migration_type=snapshot\n+\n+        # Migrate 5x Record entities only\n+        http://localhost:8080/data_migration?rand_var=898230981029381274897247&test_only=true&migration_type=record\n+\n+        # Migrate all snapshots\n+        http://localhost:8080/data_migration?rand_var=898230981029381274897247&test_only=false&migration_type=snapshot\n+\n+        # Migrate all records\n+        http://localhost:8080/data_migration?rand_var=898230981029381274897247&test_only=false&migration_type=record\n+\n+        Args:\n+            rand_var: Must == 898230981029381274897247, just in case does hit prod\n+            env_only: \"true\" or \"false\", whether to populate only the environment\n+                      variables (no fake data)\n+\n+        Returns:\n+            HTTP 200 if successful \n+            HTTP 400 if not\n+        \"\"\"\n+        # Get and validate params\n+        rand_var = self.request.get('rand_var', None)\n+        test_only = self.request.get('test_only', \"true\").lower()\n+        migration_type = self.request.get('migration_type', \"snapshot\").lower()\n+\n+        test_only = True if test_only == \"true\" else False\n+\n+        if rand_var != \"898230981029381274897247\":\n+            logging.error(\"Data migration request missing safeguard 'rand_var',\"\n+                          \" exiting.\")\n+            self.response.write(\"Invalid parameters, see logs.\")\n+            self.response.set_status(500)\n+            return\n+\n+        if migration_type == \"snapshot\":\n+            if test_only:\n+                migrate_snapshots(batch_size=TEST_BATCH_SIZE, test_only=True)\n+            else:\n+                deferred.defer(migrate_snapshots, test_only=False)\n+        elif migration_type == \"record\":\n+            if test_only:\n+                migrate_record_fields(batch_size=TEST_BATCH_SIZE, test_only=True)\n+            else:\n+                deferred.defer(migrate_record_fields, test_only=False)\n+        else:\n+            logging.error(\"Migration type '%s' not recognized. Exiting.\" % \n+                migration_type)\n+            self.response.write(\"Invalid parameters, see logs.\")\n+            self.response.set_status(500)\n+            return\n+\n+        self.response.write(\"Kicked off migration.\")\n+        logging.info(\"Kicked off migration.\")\n+\n+\n+def migrate_snapshots(cursor=None, num_updated=0, batch_size=100, test_only=True):\n+    \"\"\"\n+    migrate_snapshots()\n+    Migrate InmateFacilitySnapshots into InmateSnapshot records. Reads in 50\n+    InmateFacilitySnapshots at a time, pulls the relevant fields from the\n+    corresponding UsNyRecord entities, and creates corresponding 50 \n+    InmateSnapshots. Updates the InmateFacilitySnapshots to migrated=True\n+    in the same transaction.\n+\n+    Args:\n+        cursor: Query cursor for where we are in the migration\n+        num_updated: Current number of records updated\n+        batch_size: Number of records to handle during this run\n+        test_only: If true, performs non-destructive migration only\n+\n+    Returns:\n+        True if completes migration of all InmateFacilitySnapshots\n+    \"\"\"\n+    # Get (entity_count)x InmateFacilitySnapshots. If <50 returned, set \n+    # migration_complete to True.\n+    inmate_facility_query = InmateFacilitySnapshot.query()\n+    inmate_facility_snapshots, next_cursor, more = inmate_facility_query.fetch_page(\n+        batch_size, start_cursor=cursor)\n+\n+    to_put = []\n+    to_del = []\n+\n+    # For each snapshot, pull date and facility info, parent record info, and\n+    # create new InmateSnapshot entity. Add to new_snapshot_entities.\n+    for facility_snapshot in inmate_facility_snapshots:\n+        record_key = facility_snapshot.key.parent()\n+        record = record_key.get()\n+\n+        inmate_snapshot = UsNyInmateSnapshot(parent=record_key,","path":"scraper/data_migration.py","position":132,"original_position":131,"commit_id":"05fad1aa8c50421baff6915b60c0895dd653cb0a","original_commit_id":"f803c6d18e7e03dff7a1021b16b862f8537ebd1c","user":{"login":"cawarren","id":59526,"avatar_url":"https://avatars2.githubusercontent.com/u/59526?v=4","gravatar_id":"","url":"https://api.github.com/users/cawarren","html_url":"https://github.com/cawarren","followers_url":"https://api.github.com/users/cawarren/followers","following_url":"https://api.github.com/users/cawarren/following{/other_user}","gists_url":"https://api.github.com/users/cawarren/gists{/gist_id}","starred_url":"https://api.github.com/users/cawarren/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cawarren/subscriptions","organizations_url":"https://api.github.com/users/cawarren/orgs","repos_url":"https://api.github.com/users/cawarren/repos","events_url":"https://api.github.com/users/cawarren/events{/privacy}","received_events_url":"https://api.github.com/users/cawarren/received_events","type":"User","site_admin":false},"body":"I've updated this to only store changed fields in the snapshots. This means to get the full inmate information, you can just pull the Inmate or Record entities (which are guaranteed to have the last thing we saw in them).\r\n\r\nIf you want instead to see when something changed, or every value something's had and the time intervals in between (e.g., each facility an inmate was in for a sentence and when we noted a change throughout that sentence), you can just request snapshots which are children of the relevant record (or grandchildren of the inmate), and which have a non-None entry for the specific field.\r\n\r\nI think this will be both more efficient and easier to piece together inmate trajectories from. ","created_at":"2018-04-12T19:26:08Z","updated_at":"2018-04-12T19:26:08Z","html_url":"https://github.com/Recidiviz/recidiviz-data/pull/46#discussion_r181195447","pull_request_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/comments/181195447"},"html":{"href":"https://github.com/Recidiviz/recidiviz-data/pull/46#discussion_r181195447"},"pull_request":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46"}},"in_reply_to_id":167397227},"pull_request":{"url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46","id":167521738,"html_url":"https://github.com/Recidiviz/recidiviz-data/pull/46","diff_url":"https://github.com/Recidiviz/recidiviz-data/pull/46.diff","patch_url":"https://github.com/Recidiviz/recidiviz-data/pull/46.patch","issue_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/46","number":46,"state":"open","locked":false,"title":"Merge ny-rescrape back to Master","user":{"login":"cawarren","id":59526,"avatar_url":"https://avatars2.githubusercontent.com/u/59526?v=4","gravatar_id":"","url":"https://api.github.com/users/cawarren","html_url":"https://github.com/cawarren","followers_url":"https://api.github.com/users/cawarren/followers","following_url":"https://api.github.com/users/cawarren/following{/other_user}","gists_url":"https://api.github.com/users/cawarren/gists{/gist_id}","starred_url":"https://api.github.com/users/cawarren/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cawarren/subscriptions","organizations_url":"https://api.github.com/users/cawarren/orgs","repos_url":"https://api.github.com/users/cawarren/repos","events_url":"https://api.github.com/users/cawarren/events{/privacy}","received_events_url":"https://api.github.com/users/cawarren/received_events","type":"User","site_admin":false},"body":"This PR:\r\n- Adds a second type of scraping activity, *snapshot scraping.* Snapshot scraping targets only those records relevant to the timeframe we look at for recidivism calculations, and creates snapshots if there are any new changes to these records.\r\n- Adds all mutable `Record` and `UsNyRecord` fields to the `InmateSnapshot` and `UsNyInmateSnapshot` models for more detailed tracking of changes to inmate records\r\n- Migrates current `InmateFacilitySnapshot` entities into `UsNyInmateSnapshot` entities\r\n- Migrates the `last_release_date` and `last_release_type` fields from `UsNyRecord` to `latest_release_date` and `latest_release_type` fields in the superclass `Record`, as these are needed more generally for snapshot scrapes\r\n- Fixes issue 28\r\n\r\nChanges to this PR, prior to merge:\r\n- Remove test database request handler (provided as aid to testing changes)\r\n\r\nFollow-ups to this PR:\r\n- Remove data migration request handler and `deferred` builtin support\r\n- Remove `InmateFacilitySnapshot` model \r\n- Add snapshot scraping cron jobs at a reasonable frequency, and a time of day that won't conflict with background scraping.\r\n\r\n**Overview of changes**\r\n\r\n**Scraper control** has been consolidated into `queue_control.py`, centralizing the start/stop/resumption of scrapers. The `recidiviz.py` file that used to contain start logic for scrapers no longer exists.\r\n\r\n**Snapshot Scraping** For newly started scrapes, the `queue_control` module generates a list of targets - names for background scrapes, and inmates for the snapshot scrapes (we focus on inmates instead of records, because we want to find any new records we don't yet know about for the inmates who have records relevant to calculator). For snapshot scrapes, we also generate an 'ignore list' - a set of records that the scraper shouldn't bother scraping, because they're known and too old to be relevant to our metrics.\r\n\r\nFor either scrape type, the resulting target lists are dumped into the datastore as `DocketItem`s for that particular scrape type. The scraper logic now picks up an arbitrary `DocketItem` for its scrape_type, scrapes that item, then deletes the processed `DocketItem` and selects a new item from the docket. The scrape is complete when no `DocketItems` for the selected scrape type remain.\r\n\r\n**Migration** The `data_migration` module supports two migration types: `record` and `snapshot`. See the *This PR* section above for the specific changes each migration type makes. \r\n\r\nThe migration tool is destructive - it handles both migration and deletion of the data that's been migrated. It supports a `test_only` URL flag, which will limit the number of entities migrated to 5 and migrate non-destructively.\r\n\r\nExample queries are included in comments at the top of the `data_migration` module.\r\n\r\n**Fake data population** The `populate_test_db` module is provided for simpler population of environment variables and fake data, for testing both the data migration and the updated snapshot and background scrape behavior.\r\n\r\nExample queries for different combinations of fake data population are provided in comments at the top of the module.","created_at":"2018-02-06T20:29:47Z","updated_at":"2018-04-12T19:26:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4fdb872488fda5b06025c807fa4d27d455414038","assignee":{"login":"cawarren","id":59526,"avatar_url":"https://avatars2.githubusercontent.com/u/59526?v=4","gravatar_id":"","url":"https://api.github.com/users/cawarren","html_url":"https://github.com/cawarren","followers_url":"https://api.github.com/users/cawarren/followers","following_url":"https://api.github.com/users/cawarren/following{/other_user}","gists_url":"https://api.github.com/users/cawarren/gists{/gist_id}","starred_url":"https://api.github.com/users/cawarren/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cawarren/subscriptions","organizations_url":"https://api.github.com/users/cawarren/orgs","repos_url":"https://api.github.com/users/cawarren/repos","events_url":"https://api.github.com/users/cawarren/events{/privacy}","received_events_url":"https://api.github.com/users/cawarren/received_events","type":"User","site_admin":false},"assignees":[{"login":"cawarren","id":59526,"avatar_url":"https://avatars2.githubusercontent.com/u/59526?v=4","gravatar_id":"","url":"https://api.github.com/users/cawarren","html_url":"https://github.com/cawarren","followers_url":"https://api.github.com/users/cawarren/followers","following_url":"https://api.github.com/users/cawarren/following{/other_user}","gists_url":"https://api.github.com/users/cawarren/gists{/gist_id}","starred_url":"https://api.github.com/users/cawarren/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cawarren/subscriptions","organizations_url":"https://api.github.com/users/cawarren/orgs","repos_url":"https://api.github.com/users/cawarren/repos","events_url":"https://api.github.com/users/cawarren/events{/privacy}","received_events_url":"https://api.github.com/users/cawarren/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46/commits","review_comments_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46/comments","review_comment_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/46/comments","statuses_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/statuses/05fad1aa8c50421baff6915b60c0895dd653cb0a","head":{"label":"Recidiviz:ny-rescrape","ref":"ny-rescrape","sha":"05fad1aa8c50421baff6915b60c0895dd653cb0a","user":{"login":"Recidiviz","id":35868122,"avatar_url":"https://avatars2.githubusercontent.com/u/35868122?v=4","gravatar_id":"","url":"https://api.github.com/users/Recidiviz","html_url":"https://github.com/Recidiviz","followers_url":"https://api.github.com/users/Recidiviz/followers","following_url":"https://api.github.com/users/Recidiviz/following{/other_user}","gists_url":"https://api.github.com/users/Recidiviz/gists{/gist_id}","starred_url":"https://api.github.com/users/Recidiviz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Recidiviz/subscriptions","organizations_url":"https://api.github.com/users/Recidiviz/orgs","repos_url":"https://api.github.com/users/Recidiviz/repos","events_url":"https://api.github.com/users/Recidiviz/events{/privacy}","received_events_url":"https://api.github.com/users/Recidiviz/received_events","type":"Organization","site_admin":false},"repo":{"id":108306686,"name":"recidiviz-data","full_name":"Recidiviz/recidiviz-data","owner":{"login":"Recidiviz","id":35868122,"avatar_url":"https://avatars2.githubusercontent.com/u/35868122?v=4","gravatar_id":"","url":"https://api.github.com/users/Recidiviz","html_url":"https://github.com/Recidiviz","followers_url":"https://api.github.com/users/Recidiviz/followers","following_url":"https://api.github.com/users/Recidiviz/following{/other_user}","gists_url":"https://api.github.com/users/Recidiviz/gists{/gist_id}","starred_url":"https://api.github.com/users/Recidiviz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Recidiviz/subscriptions","organizations_url":"https://api.github.com/users/Recidiviz/orgs","repos_url":"https://api.github.com/users/Recidiviz/repos","events_url":"https://api.github.com/users/Recidiviz/events{/privacy}","received_events_url":"https://api.github.com/users/Recidiviz/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Recidiviz/recidiviz-data","description":"Ingesting prison data from inmate locator systems and calculating recidivism","fork":false,"url":"https://api.github.com/repos/Recidiviz/recidiviz-data","forks_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/forks","keys_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/teams","hooks_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/hooks","issue_events_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/events{/number}","events_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/events","assignees_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/assignees{/user}","branches_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/branches{/branch}","tags_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/tags","blobs_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/refs{/sha}","trees_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/statuses/{sha}","languages_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/languages","stargazers_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/stargazers","contributors_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/contributors","subscribers_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/subscribers","subscription_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/subscription","commits_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/commits{/sha}","git_commits_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/commits{/sha}","comments_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/comments{/number}","issue_comment_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/comments{/number}","contents_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/contents/{+path}","compare_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/merges","archive_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/downloads","issues_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues{/number}","pulls_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls{/number}","milestones_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/milestones{/number}","notifications_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/labels{/name}","releases_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/releases{/id}","deployments_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/deployments","created_at":"2017-10-25T18:04:59Z","updated_at":"2018-04-01T22:25:13Z","pushed_at":"2018-04-12T19:20:06Z","git_url":"git://github.com/Recidiviz/recidiviz-data.git","ssh_url":"git@github.com:Recidiviz/recidiviz-data.git","clone_url":"https://github.com/Recidiviz/recidiviz-data.git","svn_url":"https://github.com/Recidiviz/recidiviz-data","homepage":"","size":2168,"stargazers_count":2,"watchers_count":2,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":23,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0"},"forks":1,"open_issues":23,"watchers":2,"default_branch":"master"}},"base":{"label":"Recidiviz:master","ref":"master","sha":"641a9e792e69eaaf889e460892e31e2fdfcce734","user":{"login":"Recidiviz","id":35868122,"avatar_url":"https://avatars2.githubusercontent.com/u/35868122?v=4","gravatar_id":"","url":"https://api.github.com/users/Recidiviz","html_url":"https://github.com/Recidiviz","followers_url":"https://api.github.com/users/Recidiviz/followers","following_url":"https://api.github.com/users/Recidiviz/following{/other_user}","gists_url":"https://api.github.com/users/Recidiviz/gists{/gist_id}","starred_url":"https://api.github.com/users/Recidiviz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Recidiviz/subscriptions","organizations_url":"https://api.github.com/users/Recidiviz/orgs","repos_url":"https://api.github.com/users/Recidiviz/repos","events_url":"https://api.github.com/users/Recidiviz/events{/privacy}","received_events_url":"https://api.github.com/users/Recidiviz/received_events","type":"Organization","site_admin":false},"repo":{"id":108306686,"name":"recidiviz-data","full_name":"Recidiviz/recidiviz-data","owner":{"login":"Recidiviz","id":35868122,"avatar_url":"https://avatars2.githubusercontent.com/u/35868122?v=4","gravatar_id":"","url":"https://api.github.com/users/Recidiviz","html_url":"https://github.com/Recidiviz","followers_url":"https://api.github.com/users/Recidiviz/followers","following_url":"https://api.github.com/users/Recidiviz/following{/other_user}","gists_url":"https://api.github.com/users/Recidiviz/gists{/gist_id}","starred_url":"https://api.github.com/users/Recidiviz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Recidiviz/subscriptions","organizations_url":"https://api.github.com/users/Recidiviz/orgs","repos_url":"https://api.github.com/users/Recidiviz/repos","events_url":"https://api.github.com/users/Recidiviz/events{/privacy}","received_events_url":"https://api.github.com/users/Recidiviz/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Recidiviz/recidiviz-data","description":"Ingesting prison data from inmate locator systems and calculating recidivism","fork":false,"url":"https://api.github.com/repos/Recidiviz/recidiviz-data","forks_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/forks","keys_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/teams","hooks_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/hooks","issue_events_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/events{/number}","events_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/events","assignees_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/assignees{/user}","branches_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/branches{/branch}","tags_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/tags","blobs_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/refs{/sha}","trees_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/statuses/{sha}","languages_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/languages","stargazers_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/stargazers","contributors_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/contributors","subscribers_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/subscribers","subscription_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/subscription","commits_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/commits{/sha}","git_commits_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/git/commits{/sha}","comments_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/comments{/number}","issue_comment_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/comments{/number}","contents_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/contents/{+path}","compare_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/merges","archive_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/downloads","issues_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues{/number}","pulls_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls{/number}","milestones_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/milestones{/number}","notifications_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/labels{/name}","releases_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/releases{/id}","deployments_url":"https://api.github.com/repos/Recidiviz/recidiviz-data/deployments","created_at":"2017-10-25T18:04:59Z","updated_at":"2018-04-01T22:25:13Z","pushed_at":"2018-04-12T19:20:06Z","git_url":"git://github.com/Recidiviz/recidiviz-data.git","ssh_url":"git@github.com:Recidiviz/recidiviz-data.git","clone_url":"https://github.com/Recidiviz/recidiviz-data.git","svn_url":"https://github.com/Recidiviz/recidiviz-data","homepage":"","size":2168,"stargazers_count":2,"watchers_count":2,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":23,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0"},"forks":1,"open_issues":23,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46"},"html":{"href":"https://github.com/Recidiviz/recidiviz-data/pull/46"},"issue":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/46"},"comments":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/issues/46/comments"},"review_comments":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46/comments"},"review_comment":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/pulls/46/commits"},"statuses":{"href":"https://api.github.com/repos/Recidiviz/recidiviz-data/statuses/05fad1aa8c50421baff6915b60c0895dd653cb0a"}},"author_association":"OWNER"}}
{"actor":{"display_login":"cawarren"}}