{"action":"created","comment":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/194985897","pull_request_review_id":128261831,"id":194985897,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5NDk4NTg5Nw==","diff_hunk":"@@ -0,0 +1,52 @@\n+domain: agg\n+table_id: monthy_aggregate_pregnancies\n+display_name: Pregnancy Data Per Month\n+primary_table:\n+  data_source_id: 550c3cd432d931387e75e8506b5caf9e  # the data source ID of e.g. the 'pregnancy_cases' table\n+  key_column: doc_id\n+  columns:\n+    # this should generate SQL that looks something like:\n+    # insert into pregnancy_cases... values... (\n+    #   select name as name, lmp as pregnancy_start_date, 1 as open_in_month,\n+    #   lmp < :month_end and (delivery_date is null or delivery_date >= :month_start) as pregnant_in_month\n+    # )\n+    - column_id: name\n+      # todo: unclear where (if anywhere) we want to add dimension info. this is one possible place it could go\n+      # is_dimension: false\n+      type: reference\n+      config_params:\n+        referenced_column: name\n+    - column_id: pregnancy_start_date\n+      type: reference\n+      config_params:\n+        referenced_column: lmp\n+    - column_id: open_in_month\n+      type: constant\n+      config_params:\n+        constant: 1\n+    - column_id: pregnant_in_month\n+      type: sql_statement\n+      config_params:\n+        # sql statements will only be able to access the current row\n+        datatype: small_integer\n+        statement: CASE WHEN lmp < :month_end and (delivery_date is null or delivery_date >= :month_start) THEN 1 ELSE 0 END\n+        statement_params:\n+          # these '_agg' things are magic variables that let you reference the aggregation window's start/end conditions\n+          month_start: :agg_window_start\n+          month_end: :agg_window_end\n+time_aggregation:\n+  unit: month  # unit additionally determines the datatype and the column name used\n+  # conditions for inclusion of a particular row on a particular month\n+  # start and end conditions must (currently) be based off the primary data source table\n+  start_column: opened_date  # if value is not set, defaults to excluding the row entirely\n+  end_column: closed_date  # if value is not set, defaults to excluding the row beyond the current date\n+secondary_tables:\n+  # this is a list, though in this example there is only one.\n+  - data_source_id: d824a4864ecb421fb3be8bf8173a05d7\n+    key_column: form.case.@case_id  # how rows will get joined from primary to secondary tables\n+    aggregation_column: received_on  # determines the aggregation window that each secondary row falls in","path":"corehq/apps/aggregate_ucrs/tests/data/table_definitions/monthly_aggregate_definition.yml","position":null,"original_position":47,"commit_id":"a4de2088179ccb51875c9f4d12f881d4b7ac9a5c","original_commit_id":"230f2c525aa508dab585638faaa2566a747e344f","user":{"login":"snopoke","id":249606,"node_id":"MDQ6VXNlcjI0OTYwNg==","avatar_url":"https://avatars3.githubusercontent.com/u/249606?v=4","gravatar_id":"","url":"https://api.github.com/users/snopoke","html_url":"https://github.com/snopoke","followers_url":"https://api.github.com/users/snopoke/followers","following_url":"https://api.github.com/users/snopoke/following{/other_user}","gists_url":"https://api.github.com/users/snopoke/gists{/gist_id}","starred_url":"https://api.github.com/users/snopoke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/snopoke/subscriptions","organizations_url":"https://api.github.com/users/snopoke/orgs","repos_url":"https://api.github.com/users/snopoke/repos","events_url":"https://api.github.com/users/snopoke/events{/privacy}","received_events_url":"https://api.github.com/users/snopoke/received_events","type":"User","site_admin":false},"body":"`time_window_column` is good","created_at":"2018-06-13T07:53:51Z","updated_at":"2018-06-13T07:53:51Z","html_url":"https://github.com/dimagi/commcare-hq/pull/20874#discussion_r194985897","pull_request_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/194985897"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/20874#discussion_r194985897"},"pull_request":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874"}},"in_reply_to_id":194858287},"pull_request":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874","id":194228675,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk0MjI4Njc1","html_url":"https://github.com/dimagi/commcare-hq/pull/20874","diff_url":"https://github.com/dimagi/commcare-hq/pull/20874.diff","patch_url":"https://github.com/dimagi/commcare-hq/pull/20874.patch","issue_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/20874","number":20874,"state":"open","locked":false,"title":"PoC for Aggregate UCRs","user":{"login":"czue","id":66555,"node_id":"MDQ6VXNlcjY2NTU1","avatar_url":"https://avatars3.githubusercontent.com/u/66555?v=4","gravatar_id":"","url":"https://api.github.com/users/czue","html_url":"https://github.com/czue","followers_url":"https://api.github.com/users/czue/followers","following_url":"https://api.github.com/users/czue/following{/other_user}","gists_url":"https://api.github.com/users/czue/gists{/gist_id}","starred_url":"https://api.github.com/users/czue/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/czue/subscriptions","organizations_url":"https://api.github.com/users/czue/orgs","repos_url":"https://api.github.com/users/czue/repos","events_url":"https://api.github.com/users/czue/events{/privacy}","received_events_url":"https://api.github.com/users/czue/received_events","type":"User","site_admin":false},"body":"[Largely Based off this Spec Document](https://docs.google.com/document/d/1O3p4KC1Tw251ILbCa6IIq6MPvyKTSgEgN7tHq6r_0RY/edit#)\r\n\r\nReview all at once :house: \r\n\r\nI've been working on this off and on for a while and thought it would be a good time to get some more eyes on it. :eyes: \r\n\r\nThis is a proof of concept implementation of a way to build aggregate \"UCRs\" on top of existing UCR data sources. Currently it only creates and populates the underlying database tables and does not provide any interface for reporting on top of them. \r\n\r\n## 1,000 foot view\r\n\r\nThis is almost completely encapsulated in a new HQ app.\r\n\r\n- :notebook: It's probably worth starting with the sample config files to get a sense of what this does. There's a [basic version that joins forms and cases](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/tests/data/table_definitions/basic_aggregate_definition.yml) and [another one that does monthly aggregation](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/tests/data/table_definitions/monthly_aggregate_definition.yml).\r\n- :white_check_mark: Next thing to look at is probably [the main tests](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/tests/test_aggregation.py). This will show how the configs get mapped to actual data processing via UCR data sources and form/case data.\r\n- :unicorn: Behind the scenes the spec format gets [parsed into json](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/parser.py), then [imported](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/importer.py) into [a set of django models](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/models.py) that basically map 1:1 to the spec. This is all pretty boiler-plate.\r\n- :dizzy_face: The heavy lifting and confusing part is mostly around the [data ingestion](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/ingestion.py) and supporting models. The work is all done directly in SQL via a complex dynamically-generated sqlalchemy operatoin. There are some helpers to handle [time-based aggregation](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/aggregations.py), [months](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/date_utils.py), and [different column types](https://github.com/dimagi/commcare-hq/blob/cz/aggregation/corehq/apps/aggregate_ucrs/column_specs.py). Overall the interfaces to these objects were mostly built up out of necessity based on where I wanted/needed to draw different abstraction barriers to implement the functionality I wanted. Possibly lots could be improved here.\r\n\r\n## A non-exhaustive list of things still on my radar\r\n\r\n(not necessarily trying to do all of these before this goes out, but is a list of things on my high-level todo list)\r\n\r\n- :calendar: I'd like to add support for another time-based aggregation window besides month (e.g. quarterly or daily) just to make sure that it's pretty easy to drop those in.\r\n- :lock: I'd like to fully convince myself that I haven't opened up any opportunities for SQL injection attacks\r\n- :steam_locomotive: There's a bunch of infrastructure around data processing / celery / airflow type of stuff that still needs to get built for this to actually be usable in production\r\n- :bar_chart: something in the way of visualizing the data in HQ, even if it's just exposing the underlying tables similar to the UCR export API\r\n- :heavy_plus_sign: Figuring out how we should expose the creation/editing of these models to our pilot user base. \r\n- :busts_in_silhouette: Figuring out if there's any other aggregations we want to support besides time-based. Like maybe groups or locations?\r\n- :mag: whatever comes up during code / architecture review","created_at":"2018-06-12T10:04:57Z","updated_at":"2018-06-13T07:53:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5f8fb251071f4434a33b225ef54999d3819018fc","assignee":null,"assignees":[],"requested_reviewers":[{"login":"emord","id":1471773,"node_id":"MDQ6VXNlcjE0NzE3NzM=","avatar_url":"https://avatars0.githubusercontent.com/u/1471773?v=4","gravatar_id":"","url":"https://api.github.com/users/emord","html_url":"https://github.com/emord","followers_url":"https://api.github.com/users/emord/followers","following_url":"https://api.github.com/users/emord/following{/other_user}","gists_url":"https://api.github.com/users/emord/gists{/gist_id}","starred_url":"https://api.github.com/users/emord/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emord/subscriptions","organizations_url":"https://api.github.com/users/emord/orgs","repos_url":"https://api.github.com/users/emord/repos","events_url":"https://api.github.com/users/emord/events{/privacy}","received_events_url":"https://api.github.com/users/emord/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":79032211,"node_id":"MDU6TGFiZWw3OTAzMjIxMQ==","url":"https://api.github.com/repos/dimagi/commcare-hq/labels/Open%20for%20review:%20hold%20off%20on%20merge","name":"Open for review: hold off on merge","color":"5319e7","default":false},{"id":724785066,"node_id":"MDU6TGFiZWw3MjQ3ODUwNjY=","url":"https://api.github.com/repos/dimagi/commcare-hq/labels/product/feature-flag","name":"product/feature-flag","color":"d38737","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874/commits","review_comments_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874/comments","review_comment_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/20874/comments","statuses_url":"https://api.github.com/repos/dimagi/commcare-hq/statuses/a4de2088179ccb51875c9f4d12f881d4b7ac9a5c","head":{"label":"dimagi:cz/aggregation","ref":"cz/aggregation","sha":"a4de2088179ccb51875c9f4d12f881d4b7ac9a5c","user":{"login":"dimagi","id":94514,"node_id":"MDEyOk9yZ2FuaXphdGlvbjk0NTE0","avatar_url":"https://avatars1.githubusercontent.com/u/94514?v=4","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"repo":{"id":247278,"node_id":"MDEwOlJlcG9zaXRvcnkyNDcyNzg=","name":"commcare-hq","full_name":"dimagi/commcare-hq","owner":{"login":"dimagi","id":94514,"node_id":"MDEyOk9yZ2FuaXphdGlvbjk0NTE0","avatar_url":"https://avatars1.githubusercontent.com/u/94514?v=4","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dimagi/commcare-hq","description":"A server-side tool to help manage community health workers, written in Django","fork":false,"url":"https://api.github.com/repos/dimagi/commcare-hq","forks_url":"https://api.github.com/repos/dimagi/commcare-hq/forks","keys_url":"https://api.github.com/repos/dimagi/commcare-hq/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dimagi/commcare-hq/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dimagi/commcare-hq/teams","hooks_url":"https://api.github.com/repos/dimagi/commcare-hq/hooks","issue_events_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/events{/number}","events_url":"https://api.github.com/repos/dimagi/commcare-hq/events","assignees_url":"https://api.github.com/repos/dimagi/commcare-hq/assignees{/user}","branches_url":"https://api.github.com/repos/dimagi/commcare-hq/branches{/branch}","tags_url":"https://api.github.com/repos/dimagi/commcare-hq/tags","blobs_url":"https://api.github.com/repos/dimagi/commcare-hq/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dimagi/commcare-hq/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dimagi/commcare-hq/git/refs{/sha}","trees_url":"https://api.github.com/repos/dimagi/commcare-hq/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dimagi/commcare-hq/statuses/{sha}","languages_url":"https://api.github.com/repos/dimagi/commcare-hq/languages","stargazers_url":"https://api.github.com/repos/dimagi/commcare-hq/stargazers","contributors_url":"https://api.github.com/repos/dimagi/commcare-hq/contributors","subscribers_url":"https://api.github.com/repos/dimagi/commcare-hq/subscribers","subscription_url":"https://api.github.com/repos/dimagi/commcare-hq/subscription","commits_url":"https://api.github.com/repos/dimagi/commcare-hq/commits{/sha}","git_commits_url":"https://api.github.com/repos/dimagi/commcare-hq/git/commits{/sha}","comments_url":"https://api.github.com/repos/dimagi/commcare-hq/comments{/number}","issue_comment_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/comments{/number}","contents_url":"https://api.github.com/repos/dimagi/commcare-hq/contents/{+path}","compare_url":"https://api.github.com/repos/dimagi/commcare-hq/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dimagi/commcare-hq/merges","archive_url":"https://api.github.com/repos/dimagi/commcare-hq/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dimagi/commcare-hq/downloads","issues_url":"https://api.github.com/repos/dimagi/commcare-hq/issues{/number}","pulls_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls{/number}","milestones_url":"https://api.github.com/repos/dimagi/commcare-hq/milestones{/number}","notifications_url":"https://api.github.com/repos/dimagi/commcare-hq/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dimagi/commcare-hq/labels{/name}","releases_url":"https://api.github.com/repos/dimagi/commcare-hq/releases{/id}","deployments_url":"https://api.github.com/repos/dimagi/commcare-hq/deployments","created_at":"2009-07-09T17:00:07Z","updated_at":"2018-06-12T22:15:20Z","pushed_at":"2018-06-13T07:40:48Z","git_url":"git://github.com/dimagi/commcare-hq.git","ssh_url":"git@github.com:dimagi/commcare-hq.git","clone_url":"https://github.com/dimagi/commcare-hq.git","svn_url":"https://github.com/dimagi/commcare-hq","homepage":"https://www.commcarehq.org","size":442260,"stargazers_count":92,"watchers_count":92,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":105,"mirror_url":null,"archived":false,"open_issues_count":70,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":105,"open_issues":70,"watchers":92,"default_branch":"master"}},"base":{"label":"dimagi:master","ref":"master","sha":"981574143f0761dfd2a1af2b6030dbff0f15eedf","user":{"login":"dimagi","id":94514,"node_id":"MDEyOk9yZ2FuaXphdGlvbjk0NTE0","avatar_url":"https://avatars1.githubusercontent.com/u/94514?v=4","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"repo":{"id":247278,"node_id":"MDEwOlJlcG9zaXRvcnkyNDcyNzg=","name":"commcare-hq","full_name":"dimagi/commcare-hq","owner":{"login":"dimagi","id":94514,"node_id":"MDEyOk9yZ2FuaXphdGlvbjk0NTE0","avatar_url":"https://avatars1.githubusercontent.com/u/94514?v=4","gravatar_id":"","url":"https://api.github.com/users/dimagi","html_url":"https://github.com/dimagi","followers_url":"https://api.github.com/users/dimagi/followers","following_url":"https://api.github.com/users/dimagi/following{/other_user}","gists_url":"https://api.github.com/users/dimagi/gists{/gist_id}","starred_url":"https://api.github.com/users/dimagi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dimagi/subscriptions","organizations_url":"https://api.github.com/users/dimagi/orgs","repos_url":"https://api.github.com/users/dimagi/repos","events_url":"https://api.github.com/users/dimagi/events{/privacy}","received_events_url":"https://api.github.com/users/dimagi/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dimagi/commcare-hq","description":"A server-side tool to help manage community health workers, written in Django","fork":false,"url":"https://api.github.com/repos/dimagi/commcare-hq","forks_url":"https://api.github.com/repos/dimagi/commcare-hq/forks","keys_url":"https://api.github.com/repos/dimagi/commcare-hq/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dimagi/commcare-hq/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dimagi/commcare-hq/teams","hooks_url":"https://api.github.com/repos/dimagi/commcare-hq/hooks","issue_events_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/events{/number}","events_url":"https://api.github.com/repos/dimagi/commcare-hq/events","assignees_url":"https://api.github.com/repos/dimagi/commcare-hq/assignees{/user}","branches_url":"https://api.github.com/repos/dimagi/commcare-hq/branches{/branch}","tags_url":"https://api.github.com/repos/dimagi/commcare-hq/tags","blobs_url":"https://api.github.com/repos/dimagi/commcare-hq/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dimagi/commcare-hq/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dimagi/commcare-hq/git/refs{/sha}","trees_url":"https://api.github.com/repos/dimagi/commcare-hq/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dimagi/commcare-hq/statuses/{sha}","languages_url":"https://api.github.com/repos/dimagi/commcare-hq/languages","stargazers_url":"https://api.github.com/repos/dimagi/commcare-hq/stargazers","contributors_url":"https://api.github.com/repos/dimagi/commcare-hq/contributors","subscribers_url":"https://api.github.com/repos/dimagi/commcare-hq/subscribers","subscription_url":"https://api.github.com/repos/dimagi/commcare-hq/subscription","commits_url":"https://api.github.com/repos/dimagi/commcare-hq/commits{/sha}","git_commits_url":"https://api.github.com/repos/dimagi/commcare-hq/git/commits{/sha}","comments_url":"https://api.github.com/repos/dimagi/commcare-hq/comments{/number}","issue_comment_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/comments{/number}","contents_url":"https://api.github.com/repos/dimagi/commcare-hq/contents/{+path}","compare_url":"https://api.github.com/repos/dimagi/commcare-hq/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dimagi/commcare-hq/merges","archive_url":"https://api.github.com/repos/dimagi/commcare-hq/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dimagi/commcare-hq/downloads","issues_url":"https://api.github.com/repos/dimagi/commcare-hq/issues{/number}","pulls_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls{/number}","milestones_url":"https://api.github.com/repos/dimagi/commcare-hq/milestones{/number}","notifications_url":"https://api.github.com/repos/dimagi/commcare-hq/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dimagi/commcare-hq/labels{/name}","releases_url":"https://api.github.com/repos/dimagi/commcare-hq/releases{/id}","deployments_url":"https://api.github.com/repos/dimagi/commcare-hq/deployments","created_at":"2009-07-09T17:00:07Z","updated_at":"2018-06-12T22:15:20Z","pushed_at":"2018-06-13T07:40:48Z","git_url":"git://github.com/dimagi/commcare-hq.git","ssh_url":"git@github.com:dimagi/commcare-hq.git","clone_url":"https://github.com/dimagi/commcare-hq.git","svn_url":"https://github.com/dimagi/commcare-hq","homepage":"https://www.commcarehq.org","size":442260,"stargazers_count":92,"watchers_count":92,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":105,"mirror_url":null,"archived":false,"open_issues_count":70,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":105,"open_issues":70,"watchers":92,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/20874"},"issue":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/20874"},"comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/20874/comments"},"review_comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874/comments"},"review_comment":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/20874/commits"},"statuses":{"href":"https://api.github.com/repos/dimagi/commcare-hq/statuses/a4de2088179ccb51875c9f4d12f881d4b7ac9a5c"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"snopoke"}}