{"action":"created","comment":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/50705077","id":50705077,"diff_hunk":"@@ -0,0 +1,342 @@\n+from itertools import groupby\n+from collections import defaultdict, namedtuple\n+from couchdbkit import SchemaListProperty, SchemaProperty\n+\n+from corehq.apps.userreports.expressions.getters import NestedDictGetter\n+from corehq.apps.app_manager.dbaccessors import get_built_app_ids_for_app_id\n+from corehq.apps.app_manager.models import Application\n+from dimagi.utils.couch.database import iter_docs\n+from dimagi.ext.couchdbkit import (\n+    Document,\n+    DocumentSchema,\n+    ListProperty,\n+    StringProperty,\n+    IntegerProperty,\n+)\n+\n+\n+Question = namedtuple('Question', [\n+    'type',\n+    'label',\n+    'value',\n+    'repeat',\n+    'group',\n+    'relevant',\n+    'required',\n+    'tag',\n+    'options',\n+    'translations',\n+    'calculate',\n+])\n+Question.__new__.__defaults__ = (None,) * len(Question._fields)  # Defaults all fields to None\n+\n+\n+class ExportItem(DocumentSchema):\n+    \"\"\"\n+    An item for export.\n+    path is a question path like [\"my_group\", \"q1\"] or a case property name\n+    like [\"date_of_birth\"].\n+    \"\"\"\n+    path = ListProperty()\n+    label = StringProperty()\n+    last_occurrence = IntegerProperty()\n+\n+    @classmethod\n+    def create_from_question(cls, question, appVersion):\n+        return cls(\n+            path=_string_path_to_list(question.value),\n+            label=question.label,\n+            last_occurrence=appVersion,\n+        )\n+\n+    @classmethod\n+    def merge(cls, one, two):\n+        item = cls(one.to_json())\n+        item.last_occurrence = max(one.last_occurrence, two.last_occurrence)\n+        return item\n+\n+\n+class ExportColumn(DocumentSchema):\n+    item = SchemaProperty(ExportItem)\n+    name = StringProperty()\n+    path = ListProperty()\n+    label = StringProperty()\n+    last_occurrence = IntegerProperty()\n+\n+    def get_value(self, doc, base_path):\n+        \"\"\"\n+        Get the value of self.item of the given doc.\n+        When base_path is [], doc is a form submission or case,\n+        when base_path is non empty, doc is a repeat group from a form submission.\n+        :param doc: A form submission or instance of a repeat group in a submission or case\n+        :param base_path:\n+        :return:\n+        \"\"\"\n+        # Confirm the ExportItem's path starts with the base_path\n+        assert base_path == self.item.path[:len(base_path)]\n+        # Get the path from the doc root to the desired ExportItem\n+        path = self.item.path[len(base_path):]\n+        return NestedDictGetter(path)(doc)\n+\n+\n+class TableConfiguration(DocumentSchema):\n+\n+    name = StringProperty()\n+    repeat_path = StringProperty()\n+    columns = ListProperty(ExportColumn)\n+\n+    def get_rows(self, document):\n+        \"\"\"\n+        Return a list of ExportRows generated for the given document.\n+        :param document: dictionary representation of a form submission or case\n+        :return: List of ExportRows\n+        \"\"\"\n+        # Note that repeat_items will be [document] if self.repeat_path is []\n+        repeat_items = self._get_items_for_repeat(self.repeat_path, [document])\n+        rows = []\n+        for item in repeat_items:\n+            row = ExportRow(data=[\n+                col.get_value(item, self.repeat_path) for col in self.columns\n+            ])\n+            rows.append(row)\n+        return rows\n+\n+    def _get_items_for_repeat(self, path, docs):\n+        \"\"\"\n+        Return each instance of a repeat group at the path from the given docs.\n+        If path is [], just return the docs\n+\n+        >>> TableConfiguration()._get_items_for_repeat(['foo'], [{'foo': {'bar': 'a'}}, {'foo': {'bar': 'b'}}])\n+        [{'bar': 'a'}, {'bar': 'b'}]\n+        >>> TableConfiguration()._get_items_for_repeat(['foo', 'bar'], [{'foo': [{'bar': {'baz': 'a'}}, {'bar': {'baz': 'b'}},]}]\n+        [{'baz': 'a'}, {'baz': 'b'}]\n+\n+        :param path: A list of a strings\n+        :param docs: A list of dicts representing form submissions\n+        :return:\n+        \"\"\"\n+        if len(path) == 0:\n+            return docs\n+\n+        new_docs = []\n+        for doc in docs:\n+            next_doc = doc.get(path[0], {})\n+            if type(next_doc) == list:\n+                new_docs.extend(next_doc)\n+            else:\n+                new_docs.append(next_doc)\n+        return self._get_items_for_repeat(path[1:], new_docs)\n+\n+\n+class ExportInstance(Document):\n+\n+    class Meta:\n+        app_label = 'export'\n+\n+\n+class ExportRow(object):\n+    def __init__(self, data):\n+        self.data = data\n+\n+\n+class ScalarItem(ExportItem):\n+    \"\"\"\n+    A text, numeric, date, etc. question or case property\n+    \"\"\"\n+\n+\n+class Option(DocumentSchema):\n+    \"\"\"\n+    This object represents a multiple choice question option.\n+\n+    last_occurrence is an app build number representing the last version of the app in\n+    which this option was present.\n+    \"\"\"\n+    last_occurrence = IntegerProperty()\n+    value = StringProperty()\n+\n+\n+class MultipleChoiceItem(ExportItem):\n+    \"\"\"\n+    A multiple choice question or case property\n+    Choices is the union of choices for the question in each of the builds with\n+    this question.\n+    \"\"\"\n+    options = SchemaListProperty(Option)\n+\n+    @classmethod\n+    def create_from_question(cls, question, appVersion):\n+        item = super(MultipleChoiceItem, cls).create_from_question(question, appVersion)\n+\n+        for option in question.options:\n+            item.options.append(Option(\n+                last_occurrence=appVersion,\n+                value=option['value']\n+            ))\n+        return item\n+\n+    @classmethod\n+    def merge(cls, one, two):\n+        item = super(MultipleChoiceItem, cls).merge(one, two)\n+        options = _merge_lists(one.options, two.options,\n+            keyfn=lambda i: i.value,\n+            resolvefn=lambda option1, option2:\n+                Option(\n+                    value=option1.value,\n+                    last_occurrence=max(option1.last_occurrence, option2.last_occurrence)\n+                ),\n+            copyfn=lambda option: Option(option.to_json())\n+        )\n+\n+        item.options = options\n+        return item\n+\n+\n+class ExportGroupSchema(DocumentSchema):\n+    \"\"\"\n+    An object that represents a logical group of questions in the form\n+    \"\"\"\n+    path = ListProperty()\n+    items = SchemaListProperty(ExportItem)\n+    last_occurrence = IntegerProperty()\n+\n+\n+class ExportDataSchema(DocumentSchema):\n+    \"\"\"\n+    An object representing the things that can be exported for a particular\n+    form xmlns or case type.\n+    Each item in the list is uniquely identified by its path and doc_type.\n+    repeats is a list of RepeatGroups, present at any level of the question hierarchy\n+    \"\"\"\n+    group_schemas = SchemaListProperty(ExportGroupSchema)\n+    datatype_mapping = defaultdict(lambda: ScalarItem, {\n+        'MSelect': MultipleChoiceItem,\n+    })\n+\n+    @staticmethod\n+    def generate_schema_from_builds(domain, app_id, unique_form_id):\n+        app_build_ids = get_built_app_ids_for_app_id(domain, app_id)\n+        all_xform_conf = TableConfiguration()\n+\n+        for app in iter_docs(Application.get_db(), app_build_ids):\n+            xform = app.get_form(unique_form_id).wrapped_xform()\n+            xform_conf = ExportDataSchema._generate_schema_from_xform(xform, app.langs, app.version)\n+            all_xform_conf = ExportDataSchema._merge_schema(all_xform_conf, xform_conf)\n+\n+        return all_xform_conf\n+\n+    @staticmethod\n+    def _generate_schema_from_xform(xform, langs, appVersion):\n+        questions = xform.get_questions(langs)\n+        schema = ExportDataSchema()\n+\n+        for group_path, group_questions in groupby(questions, lambda q: q['repeat']):\n+            # If group_path is None, that means the questions are part of the form and not a repeat group\n+            # inside of the form\n+            group_schema = ExportGroupSchema(\n+                path=_string_path_to_list(group_path),\n+                last_occurrence=appVersion,\n+            )\n+            for question in group_questions:\n+                wrapped_question = Question(**question)","path":"corehq/apps/export/models/new.py","position":241,"original_position":241,"commit_id":"a6638add566e29f945d4df7397fff60ff969346e","original_commit_id":"a6638add566e29f945d4df7397fff60ff969346e","user":{"login":"benrudolph","id":918514,"avatar_url":"https://avatars.githubusercontent.com/u/918514?v=3","gravatar_id":"","url":"https://api.github.com/users/benrudolph","html_url":"https://github.com/benrudolph","followers_url":"https://api.github.com/users/benrudolph/followers","following_url":"https://api.github.com/users/benrudolph/following{/other_user}","gists_url":"https://api.github.com/users/benrudolph/gists{/gist_id}","starred_url":"https://api.github.com/users/benrudolph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benrudolph/subscriptions","organizations_url":"https://api.github.com/users/benrudolph/orgs","repos_url":"https://api.github.com/users/benrudolph/repos","events_url":"https://api.github.com/users/benrudolph/events{/privacy}","received_events_url":"https://api.github.com/users/benrudolph/received_events","type":"User","site_admin":false},"body":"not necessary. it was more for readability. i kept having to go into `get_questions` function to see what was defined on the question since the question is just a dict. think we can remove if it's superfluous","created_at":"2016-01-25T15:13:25Z","updated_at":"2016-01-25T15:13:25Z","html_url":"https://github.com/dimagi/commcare-hq/pull/9985#discussion_r50705077","pull_request_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9985","_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/comments/50705077"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/9985#discussion_r50705077"},"pull_request":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9985"}}},"pull_request":{"url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9985","id":56678690,"html_url":"https://github.com/dimagi/commcare-hq/pull/9985","diff_url":"https://github.com/dimagi/commcare-hq/pull/9985.diff","patch_url":"https://github.com/dimagi/commcare-hq/pull/9985.patch","issue_url":"https://api.github.com/repos/dimagi/commcare-hq/issues/9985","number":9985,"state":"open","locked":false,"title":"ExportDataSchema generation","user":{"login":"benrudolph","id":918514,"avatar_url":"https://avatars.githubusercontent.com/u/918514?v=3","gravatar_id":"","url":"https://api.github.com/users/benrudolph","html_url":"https://github.com/benrudolph","followers_url":"https://api.github.com/users/benrudolph/followers","following_url":"https://api.github.com/users/benrudolph/following{/other_user}","gists_url":"https://api.github.com/users/benrudolph/gists{/gist_id}","starred_url":"https://api.github.com/users/benrudolph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benrudolph/subscriptions","organizations_url":"https://api.github.com/users/benrudolph/orgs","repos_url":"https://api.github.com/users/benrudolph/repos","events_url":"https://api.github.com/users/benrudolph/events{/privacy}","received_events_url":"https://api.github.com/users/benrudolph/received_events","type":"User","site_admin":false},"body":":fish: @NoahCarnahan this is what is a basic outline of what it'd look like (tests pass)","created_at":"2016-01-20T22:41:11Z","updated_at":"2016-01-25T15:13:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4ffa5a29124bb09661f0bd61b46314ce2955438c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9985/commits","review_comments_url":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9985/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/9985/comments","statuses_url":"https://api.github.com/repos/dimagi/commcare-hq/statuses/a6638add566e29f945d4df7397fff60ff969346e","head":{"label":"dimagi:table-conf-generation","ref":"table-conf-generation","sha":"a6638add566e29f945d4df7397fff60ff969346e","user":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","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,"name":"commcare-hq","full_name":"dimagi/commcare-hq","owner":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","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":"2016-01-25T13:36:18Z","pushed_at":"2016-01-25T15:04:34Z","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":"http://www.commcarehq.org","size":247099,"stargazers_count":61,"watchers_count":61,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":66,"mirror_url":null,"open_issues_count":66,"forks":66,"open_issues":66,"watchers":61,"default_branch":"master"}},"base":{"label":"dimagi:sql-export-2","ref":"sql-export-2","sha":"551e97f2b868420cb5c37fce603f42b2f2a9eebf","user":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","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,"name":"commcare-hq","full_name":"dimagi/commcare-hq","owner":{"login":"dimagi","id":94514,"avatar_url":"https://avatars.githubusercontent.com/u/94514?v=3","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":"2016-01-25T13:36:18Z","pushed_at":"2016-01-25T15:04:34Z","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":"http://www.commcarehq.org","size":247099,"stargazers_count":61,"watchers_count":61,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":66,"mirror_url":null,"open_issues_count":66,"forks":66,"open_issues":66,"watchers":61,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9985"},"html":{"href":"https://github.com/dimagi/commcare-hq/pull/9985"},"issue":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/9985"},"comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/issues/9985/comments"},"review_comments":{"href":"https://api.github.com/repos/dimagi/commcare-hq/pulls/9985/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/9985/commits"},"statuses":{"href":"https://api.github.com/repos/dimagi/commcare-hq/statuses/a6638add566e29f945d4df7397fff60ff969346e"}}}}