{"action":"created","comment":{"url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments/40578021","id":40578021,"diff_hunk":"@@ -1,120 +1,144 @@\n+\"\"\"OAuth service models\"\"\"\n+\n+import json\n+\n from django.db import models\n+from django.conf import settings\n from django.contrib.auth.models import User\n from django.utils.translation import ugettext_lazy as _\n+from django.core.validators import URLValidator\n+from django.core.urlresolvers import reverse\n \n-from .managers import BitbucketTeamManager\n-from .managers import BitbucketProjectManager\n-from .managers import GithubOrganizationManager\n-from .managers import GithubProjectManager\n-\n-\n-class GithubOrganization(models.Model):\n-    # Auto fields\n-    pub_date = models.DateTimeField(_('Publication date'), auto_now_add=True)\n-    modified_date = models.DateTimeField(_('Modified date'), auto_now=True)\n+from readthedocs.projects.constants import REPO_CHOICES\n+from readthedocs.projects.models import Project\n \n-    users = models.ManyToManyField(User, verbose_name=_('Users'),\n-                                   related_name='github_organizations')\n+from .constants import OAUTH_SOURCE\n+from .managers import RemoteRepositoryManager, RemoteOrganizationManager\n \n-    login = models.CharField(_('Login'), max_length=255, unique=True)\n \n-    email = models.EmailField(_('Email'), max_length=255, null=True, blank=True)\n+DEFAULT_PRIVACY_LEVEL = getattr(settings, 'DEFAULT_PRIVACY_LEVEL', 'public')\n \n-    name = models.CharField(_('Name'), max_length=255, null=True, blank=True)\n-    html_url = models.URLField(_('HTML URL'), max_length=200, null=True, blank=True)\n-    active = models.BooleanField(_('Active'), default=False)\n-    json = models.TextField('JSON')\n \n-    objects = GithubOrganizationManager()\n+class RemoteOrganization(models.Model):\n \n-    def __unicode__(self):\n-        return \"GitHub Organization: %s\" % (self.html_url)\n+    \"\"\"Organization from remote service\n \n+    This encapsulates both Github and Bitbucket\n+    \"\"\"\n \n-class GithubProject(models.Model):\n     # Auto fields\n     pub_date = models.DateTimeField(_('Publication date'), auto_now_add=True)\n     modified_date = models.DateTimeField(_('Modified date'), auto_now=True)\n \n-    # This should now be a OneToOne\n     users = models.ManyToManyField(User, verbose_name=_('Users'),\n-                                   related_name='github_projects')\n-    organization = models.ForeignKey(GithubOrganization, verbose_name=_('Organization'),\n-                                     related_name='projects', null=True, blank=True)\n-    name = models.CharField(_('Name'), max_length=255)\n-    full_name = models.CharField(_('Full Name'), max_length=255)\n-    description = models.TextField(_('Description'), blank=True, null=True,\n-                                   help_text=_('The reStructuredText description of the project'))\n-    git_url = models.CharField(_('Git URL'), max_length=200, blank=True)\n-    ssh_url = models.CharField(_('SSH URL'), max_length=200, blank=True)\n-    html_url = models.URLField(_('HTML URL'), max_length=200, null=True, blank=True)\n+                                   related_name='oauth_organizations')\n     active = models.BooleanField(_('Active'), default=False)\n-    json = models.TextField('JSON')\n-\n-    objects = GithubProjectManager()\n-\n-    def __unicode__(self):\n-        return \"GitHub Project: %s\" % (self.html_url)\n-\n-    def is_admin(self):\n-        full_json = eval(self.json)\n-        if 'permissions' in full_json:\n-            return full_json['permissions']['admin']\n-        return False\n \n-    def is_private(self):\n-        full_json = eval(self.json)\n-        return full_json['private']\n+    slug = models.CharField(_('Slug'), max_length=255, unique=True)\n+    name = models.CharField(_('Name'), max_length=255, null=True, blank=True)\n+    email = models.EmailField(_('Email'), max_length=255, null=True, blank=True)\n+    avatar_url = models.URLField(_('Avatar image URL'), null=True, blank=True)\n+    url = models.URLField(_('URL to organization page'), max_length=200,\n+                          null=True, blank=True)\n \n+    source = models.CharField(_('Repository source'), max_length=16,\n+                              choices=OAUTH_SOURCE)\n+    json = models.TextField(_('Serialized API response'))\n \n-class BitbucketTeam(models.Model):\n-    # Auto fields\n-    pub_date = models.DateTimeField(_('Publication date'), auto_now_add=True)\n-    modified_date = models.DateTimeField(_('Modified date'), auto_now=True)\n+    objects = RemoteOrganizationManager()\n \n-    users = models.ManyToManyField(User, verbose_name=_('Users'),\n-                                   related_name='bitbucket_organizations')\n+    def __unicode__(self):\n+        return \"Remote Organization: %s\" % (self.url)\n \n-    login = models.CharField(_('Login'), max_length=255, unique=True)\n+    def get_serialized(self, key=None, default=None):\n+        try:\n+            data = json.loads(self.json)\n+            if key is not None:\n+                return data.get(key, default)\n+            return data\n+        except ValueError:\n+            pass\n \n-    email = models.EmailField(_('Email'), max_length=255, null=True, blank=True)\n \n-    name = models.CharField(_('Name'), max_length=255, null=True, blank=True)\n-    html_url = models.URLField(_('HTML URL'), max_length=200, null=True, blank=True)\n-    active = models.BooleanField(_('Active'), default=False)\n-    json = models.TextField('JSON')\n+class RemoteRepository(models.Model):\n \n-    objects = BitbucketTeamManager()\n-\n-    def __unicode__(self):\n-        return \"Bitbucket Team: %s\" % (self.html_url)\n+    \"\"\"Remote importable repositories\n \n+    This models Github and Bitbucket importable repositories\n+    \"\"\"\n \n-class BitbucketProject(models.Model):\n     # Auto fields\n     pub_date = models.DateTimeField(_('Publication date'), auto_now_add=True)\n     modified_date = models.DateTimeField(_('Modified date'), auto_now=True)\n \n+    # This should now be a OneToOne\n     users = models.ManyToManyField(User, verbose_name=_('Users'),\n-                                   related_name='bitbucket_projects')\n-    organization = models.ForeignKey(BitbucketTeam, verbose_name=_('Organization'),\n-                                     related_name='projects', null=True, blank=True)\n-    name = models.CharField(_('Name'), max_length=255)\n-    full_name = models.CharField(_('Full Name'), max_length=255, unique=True)\n-    description = models.TextField(_('Description'), blank=True, null=True,\n-                                   help_text=_('The reStructuredText description of the project'))\n-    vcs = models.CharField(_('vcs'), max_length=200, blank=True)\n-    git_url = models.CharField(_('Git URL'), max_length=200, blank=True)\n-    ssh_url = models.CharField(_('SSH URL'), max_length=200, blank=True)\n-    html_url = models.URLField(_('HTML URL'), max_length=200, null=True, blank=True)\n+                                   related_name='oauth_repositories')\n+    organization = models.ForeignKey(\n+        RemoteOrganization, verbose_name=_('Organization'),\n+        related_name='repositories', null=True, blank=True)\n     active = models.BooleanField(_('Active'), default=False)\n-    json = models.TextField('JSON')\n \n-    objects = BitbucketProjectManager()\n+    name = models.CharField(_('Name'), max_length=255)\n+    full_name = models.CharField(_('Full Name'), max_length=255)\n+    description = models.TextField(_('Description'), blank=True, null=True,\n+                                   help_text=_('Description of the project'))\n+    avatar_url = models.URLField(_('Owner avatar image URL'), null=True,\n+                                 blank=True)\n+\n+    ssh_url = models.URLField(_('SSH URL'), max_length=512, blank=True,\n+                              validators=[URLValidator(schemes=['ssh'])])\n+    clone_url = models.URLField(\n+        _('Repository clone URL'),\n+        max_length=512,\n+        blank=True,\n+        validators=[\n+            URLValidator(schemes=['http', 'https', 'ssh', 'git', 'svn'])])\n+    html_url = models.URLField(_('HTML URL'), null=True, blank=True)\n+\n+    private = models.BooleanField(_('Private repository'), default=False)\n+    admin = models.BooleanField(_('Has admin privilege'), default=False)\n+    vcs = models.CharField(_('vcs'), max_length=200, blank=True,\n+                           choices=REPO_CHOICES)\n+\n+    source = models.CharField(_('Repository source'), max_length=16,\n+                              choices=OAUTH_SOURCE)\n+    json = models.TextField(_('Serialized API response'))\n+\n+    objects = RemoteRepositoryManager()\n+\n+    class Meta:\n+        ordering = ['organization__name', 'name']\n+        verbose_name_plural = 'remote repositories'\n \n     def __unicode__(self):\n-        return \"Bitbucket Project: %s\" % (self.html_url)\n-\n-    def is_private(self):\n-        full_json = eval(self.json)\n-        return full_json['is_private']\n+        return \"Remote repository: %s\" % (self.html_url)\n+\n+    def get_serialized(self, key=None, default=None):\n+        try:\n+            data = json.loads(self.json)\n+            if key is not None:\n+                return data.get(key, default)\n+            return data\n+        except ValueError:\n+            pass\n+\n+    def matches(self, user):\n+        \"\"\"Projects that exist with repository URL already\"\"\"\n+\n+        # TODO\n+        # ghetto_repo = self.clone_url.replace('git://', '').replace('.git', '')\n+        projects = (Project\n+                    .objects\n+                    .public(user)\n+                    .filter(repo=self.clone_url))\n+        #            .filter(Q(repo__endswith=ghetto_repo) |\n+        #                    Q(repo__endswith=ghetto_repo + '.git')))\n+        # if projects:\n+        #    repo.matches = [project.slug for project in projects]\n+        # else:\n+        #    repo.matches = []","path":"readthedocs/oauth/models.py","position":223,"original_position":223,"commit_id":"38a0045adb450830c3127a25b6335b262956f48e","original_commit_id":"b5a544f63dbf222c17ea2d955eafc391391ecaaf","user":{"login":"agjohnson","id":1140183,"avatar_url":"https://avatars.githubusercontent.com/u/1140183?v=3","gravatar_id":"","url":"https://api.github.com/users/agjohnson","html_url":"https://github.com/agjohnson","followers_url":"https://api.github.com/users/agjohnson/followers","following_url":"https://api.github.com/users/agjohnson/following{/other_user}","gists_url":"https://api.github.com/users/agjohnson/gists{/gist_id}","starred_url":"https://api.github.com/users/agjohnson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/agjohnson/subscriptions","organizations_url":"https://api.github.com/users/agjohnson/orgs","repos_url":"https://api.github.com/users/agjohnson/repos","events_url":"https://api.github.com/users/agjohnson/events{/privacy}","received_events_url":"https://api.github.com/users/agjohnson/received_events","type":"User","site_admin":false},"body":"I could also be okay saying that if you didn't import via github, we don't disallow import again from github connected accounts. The most correct solution here is an intermediary table `Project` <-> `RemoteRepo` of course, which we'll have to make the same decision on when we go to run migrations there.","created_at":"2015-09-28T17:13:20Z","updated_at":"2015-09-28T17:13:20Z","html_url":"https://github.com/rtfd/readthedocs.org/pull/1695#discussion_r40578021","pull_request_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695","_links":{"self":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments/40578021"},"html":{"href":"https://github.com/rtfd/readthedocs.org/pull/1695#discussion_r40578021"},"pull_request":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695"}}},"pull_request":{"url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695","id":45966671,"html_url":"https://github.com/rtfd/readthedocs.org/pull/1695","diff_url":"https://github.com/rtfd/readthedocs.org/pull/1695.diff","patch_url":"https://github.com/rtfd/readthedocs.org/pull/1695.patch","issue_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1695","number":1695,"state":"open","locked":false,"title":"Improve import project user experience","user":{"login":"agjohnson","id":1140183,"avatar_url":"https://avatars.githubusercontent.com/u/1140183?v=3","gravatar_id":"","url":"https://api.github.com/users/agjohnson","html_url":"https://github.com/agjohnson","followers_url":"https://api.github.com/users/agjohnson/followers","following_url":"https://api.github.com/users/agjohnson/following{/other_user}","gists_url":"https://api.github.com/users/agjohnson/gists{/gist_id}","starred_url":"https://api.github.com/users/agjohnson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/agjohnson/subscriptions","organizations_url":"https://api.github.com/users/agjohnson/orgs","repos_url":"https://api.github.com/users/agjohnson/repos","events_url":"https://api.github.com/users/agjohnson/events{/privacy}","received_events_url":"https://api.github.com/users/agjohnson/received_events","type":"User","site_admin":false},"body":"This makes a number of changes to a fairly deficient UI. It still doesn't\r\nprovide an interface for importing multiple projects at once, but that could be\r\nadded easily once we have a public task for importing projects.\r\n\r\nFrontend changes:\r\n\r\n* Combines import intermediate page, and github/bitbucket import pages into a single UI.\r\n* The new import page:\r\n  * Lists remote repos foremost\r\n  * Has a backup \"Manual import\" button\r\n  * Removes overly verbose interactions -- that is, don't explain the UI with words\r\n  * Makes import page into API driven interface\r\n  * Repos can filter by clicking an organization\r\n  * On no connected accounts, import page onboards user into connecting bitbucket/github\r\n  * Adds more graphical elements, like repo avatars\r\n  * Display errors on syncing problems\r\n  * Paginates\r\n\r\n![screen shot 2015-09-24 at 7 47 07 pm](https://cloud.githubusercontent.com/assets/1140183/10091859/22d8d22a-62f5-11e5-8ce2-008ecd218a02.png)\r\n\r\n![screen shot 2015-09-24 at 7 48 50 pm](https://cloud.githubusercontent.com/assets/1140183/10091872/537e5026-62f5-11e5-9136-c05503eac634.png)\r\n\r\nBackend changes:\r\n\r\n* Combine the two in the backend modeling\r\n* Combine update tasks\r\n* Tasks can return error messages\r\n* Shared model has common fields moved from JSON blob\r\n* No more `eval`, data is JSON serialized\r\n\r\nStill needed:\r\n\r\n- [x] Rename models, views, and fix migration. OAuth* is a stupid name\r\n- [x] Handle messaging around admin access better\r\n\r\nAlso, note: CSS changes particular to RTD.org are in `core.css`, which general styling is done in the LESS source. This is because RTD.com should avoid this styling, as list styling is not compatible.\r\n\r\nFixes #1670 ","created_at":"2015-09-25T00:41:54Z","updated_at":"2015-09-28T17:13:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0e8a9e544d88ade03e9670eed1ec27b8fb0dfc69","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695/commits","review_comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695/comments","review_comment_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1695/comments","statuses_url":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/38a0045adb450830c3127a25b6335b262956f48e","head":{"label":"rtfd:import-improve-ux","ref":"import-improve-ux","sha":"38a0045adb450830c3127a25b6335b262956f48e","user":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"repo":{"id":841835,"name":"readthedocs.org","full_name":"rtfd/readthedocs.org","owner":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rtfd/readthedocs.org","description":"source code to readthedocs.org","fork":false,"url":"https://api.github.com/repos/rtfd/readthedocs.org","forks_url":"https://api.github.com/repos/rtfd/readthedocs.org/forks","keys_url":"https://api.github.com/repos/rtfd/readthedocs.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rtfd/readthedocs.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rtfd/readthedocs.org/teams","hooks_url":"https://api.github.com/repos/rtfd/readthedocs.org/hooks","issue_events_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/events{/number}","events_url":"https://api.github.com/repos/rtfd/readthedocs.org/events","assignees_url":"https://api.github.com/repos/rtfd/readthedocs.org/assignees{/user}","branches_url":"https://api.github.com/repos/rtfd/readthedocs.org/branches{/branch}","tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/tags","blobs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/{sha}","languages_url":"https://api.github.com/repos/rtfd/readthedocs.org/languages","stargazers_url":"https://api.github.com/repos/rtfd/readthedocs.org/stargazers","contributors_url":"https://api.github.com/repos/rtfd/readthedocs.org/contributors","subscribers_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscribers","subscription_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscription","commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/rtfd/readthedocs.org/contents/{+path}","compare_url":"https://api.github.com/repos/rtfd/readthedocs.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rtfd/readthedocs.org/merges","archive_url":"https://api.github.com/repos/rtfd/readthedocs.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rtfd/readthedocs.org/downloads","issues_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues{/number}","pulls_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls{/number}","milestones_url":"https://api.github.com/repos/rtfd/readthedocs.org/milestones{/number}","notifications_url":"https://api.github.com/repos/rtfd/readthedocs.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rtfd/readthedocs.org/labels{/name}","releases_url":"https://api.github.com/repos/rtfd/readthedocs.org/releases{/id}","created_at":"2010-08-16T19:18:06Z","updated_at":"2015-09-27T21:42:59Z","pushed_at":"2015-09-28T17:05:07Z","git_url":"git://github.com/rtfd/readthedocs.org.git","ssh_url":"git@github.com:rtfd/readthedocs.org.git","clone_url":"https://github.com/rtfd/readthedocs.org.git","svn_url":"https://github.com/rtfd/readthedocs.org","homepage":"http://rtfd.org/","size":45512,"stargazers_count":2090,"watchers_count":2090,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":932,"mirror_url":null,"open_issues_count":148,"forks":932,"open_issues":148,"watchers":2090,"default_branch":"master"}},"base":{"label":"rtfd:master","ref":"master","sha":"03beee320d3eb0817f17041b9c60286f27ec5849","user":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"repo":{"id":841835,"name":"readthedocs.org","full_name":"rtfd/readthedocs.org","owner":{"login":"rtfd","id":366329,"avatar_url":"https://avatars.githubusercontent.com/u/366329?v=3","gravatar_id":"","url":"https://api.github.com/users/rtfd","html_url":"https://github.com/rtfd","followers_url":"https://api.github.com/users/rtfd/followers","following_url":"https://api.github.com/users/rtfd/following{/other_user}","gists_url":"https://api.github.com/users/rtfd/gists{/gist_id}","starred_url":"https://api.github.com/users/rtfd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rtfd/subscriptions","organizations_url":"https://api.github.com/users/rtfd/orgs","repos_url":"https://api.github.com/users/rtfd/repos","events_url":"https://api.github.com/users/rtfd/events{/privacy}","received_events_url":"https://api.github.com/users/rtfd/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rtfd/readthedocs.org","description":"source code to readthedocs.org","fork":false,"url":"https://api.github.com/repos/rtfd/readthedocs.org","forks_url":"https://api.github.com/repos/rtfd/readthedocs.org/forks","keys_url":"https://api.github.com/repos/rtfd/readthedocs.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rtfd/readthedocs.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rtfd/readthedocs.org/teams","hooks_url":"https://api.github.com/repos/rtfd/readthedocs.org/hooks","issue_events_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/events{/number}","events_url":"https://api.github.com/repos/rtfd/readthedocs.org/events","assignees_url":"https://api.github.com/repos/rtfd/readthedocs.org/assignees{/user}","branches_url":"https://api.github.com/repos/rtfd/readthedocs.org/branches{/branch}","tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/tags","blobs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/{sha}","languages_url":"https://api.github.com/repos/rtfd/readthedocs.org/languages","stargazers_url":"https://api.github.com/repos/rtfd/readthedocs.org/stargazers","contributors_url":"https://api.github.com/repos/rtfd/readthedocs.org/contributors","subscribers_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscribers","subscription_url":"https://api.github.com/repos/rtfd/readthedocs.org/subscription","commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/rtfd/readthedocs.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/rtfd/readthedocs.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/rtfd/readthedocs.org/contents/{+path}","compare_url":"https://api.github.com/repos/rtfd/readthedocs.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rtfd/readthedocs.org/merges","archive_url":"https://api.github.com/repos/rtfd/readthedocs.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rtfd/readthedocs.org/downloads","issues_url":"https://api.github.com/repos/rtfd/readthedocs.org/issues{/number}","pulls_url":"https://api.github.com/repos/rtfd/readthedocs.org/pulls{/number}","milestones_url":"https://api.github.com/repos/rtfd/readthedocs.org/milestones{/number}","notifications_url":"https://api.github.com/repos/rtfd/readthedocs.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rtfd/readthedocs.org/labels{/name}","releases_url":"https://api.github.com/repos/rtfd/readthedocs.org/releases{/id}","created_at":"2010-08-16T19:18:06Z","updated_at":"2015-09-27T21:42:59Z","pushed_at":"2015-09-28T17:05:07Z","git_url":"git://github.com/rtfd/readthedocs.org.git","ssh_url":"git@github.com:rtfd/readthedocs.org.git","clone_url":"https://github.com/rtfd/readthedocs.org.git","svn_url":"https://github.com/rtfd/readthedocs.org","homepage":"http://rtfd.org/","size":45512,"stargazers_count":2090,"watchers_count":2090,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":932,"mirror_url":null,"open_issues_count":148,"forks":932,"open_issues":148,"watchers":2090,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695"},"html":{"href":"https://github.com/rtfd/readthedocs.org/pull/1695"},"issue":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1695"},"comments":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/issues/1695/comments"},"review_comments":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695/comments"},"review_comment":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/pulls/1695/commits"},"statuses":{"href":"https://api.github.com/repos/rtfd/readthedocs.org/statuses/38a0045adb450830c3127a25b6335b262956f48e"}}}}