{"action":"created","comment":{"url":"https://api.github.com/repos/indico/indico/pulls/comments/58626932","id":58626932,"diff_hunk":"@@ -0,0 +1,498 @@\n+# This file is part of Indico.\n+# Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).\n+#\n+# Indico is free software; you can redistribute it and/or\n+# modify it under the terms of the GNU General Public License as\n+# published by the Free Software Foundation; either version 3 of the\n+# License, or (at your option) any later version.\n+#\n+# Indico is distributed in the hope that it will be useful, but\n+# WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+# General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with Indico; if not, see <http://www.gnu.org/licenses/>.\n+\n+from datetime import timedelta\n+\n+from indico.core import signals\n+from indico.core.db import db\n+from indico.core.db.sqlalchemy.util.session import no_autoflush\n+from indico.modules.events.abstracts.models.abstracts import Abstract\n+from indico.modules.events.abstracts.models.fields import AbstractFieldValue\n+from indico.modules.events.abstracts.models.judgments import Judgment\n+from indico.modules.events.abstracts.settings import abstracts_settings\n+from indico.modules.events.contributions.models.fields import ContributionField\n+from indico.modules.events.contributions.operations import create_contribution\n+from indico.modules.events.models.persons import EventPerson\n+from indico.modules.events.contributions.models.persons import AuthorType, ContributionPersonLink\n+from indico.modules.users.models.users import User, UserTitle\n+from indico.util.caching import memoize_request\n+from indico.util.i18n import _\n+from indico.util.string import encode_utf8, to_unicode\n+from indico.util.text import wordsCounter\n+\n+FIELD_TYPE_MAP = {\n+    'input': 'text',\n+    'textarea': 'text',\n+    'selection': 'single_choice'\n+}\n+\n+\n+@signals.event.contributions.contribution_deleted.connect\n+def _contribution_deleted(contrib, parent=None):\n+    from MaKaC.review import AbstractStatusAccepted, AbstractStatusSubmitted\n+    abstract = contrib.abstract\n+    if abstract:\n+        status = abstract.as_legacy.getCurrentStatus()\n+        if isinstance(status, AbstractStatusAccepted):\n+            if status.getTrack() is not None:\n+                abstract.as_legacy.addTrack(status.getTrack())\n+            abstract.as_legacy.setCurrentStatus(AbstractStatusSubmitted(abstract.as_legacy))\n+            abstract.contribution = None\n+\n+\n+def person_from_data(person_data, event):\n+    user = User.find_first(~User.is_deleted, User.all_emails.contains(person_data['email'].lower()))\n+    if user:\n+        return EventPerson.for_user(user, event)\n+\n+    person = EventPerson.find_first(email=person_data['email'].lower())\n+    if not person:\n+        person = EventPerson(event_new=event, **person_data)\n+    return person\n+\n+\n+@no_autoflush\n+def contribution_from_abstract(abstract, sess):\n+    from MaKaC.review import AbstractStatusAccepted\n+\n+    event = abstract.as_new.event_new\n+    duration = sess.default_contribution_duration if sess else timedelta(minutes=15)\n+    links = []\n+\n+    for auth in abstract.getAuthorList():\n+        author_type = AuthorType.primary if abstract.isPrimaryAuthor(auth) else AuthorType.secondary\n+        person_data = {\n+            'title': UserTitle.from_legacy(to_unicode(auth.getTitle())),\n+            'email': to_unicode(auth.getEmail()),\n+            'address': to_unicode(auth.getAddress()),\n+            'affiliation': to_unicode(auth.getAffiliation()),\n+            'first_name': to_unicode(auth.getFirstName()),\n+            'last_name': to_unicode(auth.getSurName()),\n+            'phone': to_unicode(auth.getTelephone())\n+        }\n+\n+        person = person_from_data(person_data, event)\n+        person_data.pop('email')\n+        links.append(ContributionPersonLink(author_type=author_type, person=person,\n+                                            is_speaker=abstract.isSpeaker(auth), **person_data))\n+\n+    custom_fields_data = [{field_val.contribution_field.id: field_val.data} for\n+                          field_val in abstract.as_new.field_values]\n+    contrib = create_contribution(event, {'title': abstract.getTitle(), 'duration': duration,\n+                                          'person_link_data': {link: True for link in links}},\n+                                  custom_fields_data=custom_fields_data)\n+    contrib.abstract = abstract.as_new\n+    contrib.description = abstract.as_new.description\n+\n+    # if this is an accepted abstract, set track and type if present\n+    status = abstract.getCurrentStatus()\n+    if isinstance(status, AbstractStatusAccepted):\n+        track = abstract.as_new.accepted_track\n+        contrib_type = abstract.as_new.accepted_type\n+        if track:\n+            contrib.track_id = int(track.getId())\n+        if contrib_type:\n+            contrib.type = contrib_type\n+\n+    if sess:\n+        contrib.session = sess\n+\n+    return contrib\n+\n+\n+class AbstractFieldWrapper(object):\n+    \"\"\"Wraps an actual ``ContributionField`` in order to emulate the old ``AbstractField``s.\"\"\"\n+\n+    @classmethod\n+    def wrap(cls, field):\n+        if field.field_type == 'single_choice':\n+            return AbstractSelectionFieldWrapper(field)\n+        return AbstractTextFieldWrapper(field)\n+\n+    def __init__(self, contrib_field):\n+        self.field = contrib_field\n+\n+    def getType(self):\n+        field = self.field\n+\n+        if field.field_type == 'text':\n+            return 'textarea' if field.field_data.get('multiline') else 'input'\n+        else:\n+            return 'selection'\n+\n+    def isMandatory(self):\n+        return self.field.is_required\n+\n+    def getId(self):\n+        return self.field.legacy_id or str(self.field.id)\n+\n+    def getCaption(self):\n+        return self.field.title\n+\n+    def isActive(self):\n+        return self.field.is_active\n+\n+    def getValues(self):\n+        data = self.field.field_data\n+        data.update({\n+            'id': self.field.id,\n+            'caption': self.field.title.encode('utf-8'),\n+            'isMandatory': self.field.is_required\n+        })\n+        return data\n+\n+    def check(self, content):\n+        if self.field.is_active and self.field.is_required and not content:\n+            return [_(\"The field '{}' is mandatory\").format(self.field.title)]\n+        return []\n+\n+\n+class AbstractTextFieldWrapper(AbstractFieldWrapper):\n+    \"\"\"Wraps legacy ``AbstractTextAreaField`` and ``AbstractInputField``.\"\"\"\n+\n+    @property\n+    def max_words(self):\n+        return self.field.field_data.get('max_words', 0)\n+\n+    @property\n+    def max_chars(self):\n+        return self.field.field_data.get('max_chars', 0)\n+\n+    def getLimitation(self):\n+        return 'chars' if self.max_words is None else 'words'\n+\n+    def getMaxLength(self):\n+        return self.max_words if self.max_chars is None else self.max_chars\n+\n+    def getValues(self):\n+        data = super(AbstractTextFieldWrapper, self).getValues()\n+        data.update({\n+            'maxLength': self.getMaxLength(),\n+            'limitation': self.getLimitation()\n+        })\n+        return data\n+\n+    def check(self, content):\n+        errors = super(AbstractTextFieldWrapper, self).check(content)\n+\n+        if self.max_words and wordsCounter(str(content)) > self.max_words:\n+            errors.append(_(\"The field '{}' cannot be more than {} words long\").format(\n+                self.field.title, self.max_words))\n+        elif self.max_chars and len(content) > self.max_chars:\n+            errors.append(_(\"The field '{}' cannot be more than {} characters long\").format(\n+                self.field.title, self.max_chars))\n+\n+        return errors\n+\n+\n+class AbstractDescriptionFieldProxy(object):\n+    \"\"\"Simulates an ``AbstractTextField``\"\"\"\n+\n+    def __init__(self, event):\n+        self.event = event\n+\n+    @property\n+    def legacy_id(self):\n+        return 'content'\n+\n+    @property\n+    def title(self):\n+        return _('Content')\n+\n+    @property\n+    def is_active(self):\n+        return abstracts_settings.get(self.event, 'description_settings')['active']\n+\n+    @property\n+    def field_type(self):\n+        return 'text'\n+\n+    @property\n+    def field_data(self):\n+        settings = abstracts_settings.get(self.event, 'description_settings')\n+        return {\n+            'max_words': settings['max_words'],\n+            'max_chars': settings['max_chars'],\n+            'multiline': True\n+        }\n+\n+    @property\n+    def is_required(self):\n+        return abstracts_settings.get(self.event, 'description_settings')['required']\n+\n+\n+class AbstractSelectionFieldWrapper(AbstractFieldWrapper):\n+    \"\"\"Wraps legacy ``AbstractSelectionField``.\"\"\"\n+\n+    def getOption(self, option_id):\n+        return next((SelectedFieldOptionWrapper(option)\n+                     for option in self.field.field_data['options'] if option['id'] == option_id), None)\n+\n+    def getOptions(self):\n+        return [SelectedFieldOptionWrapper(option) for option in self.field.field_data['options']]\n+\n+    def getValues(self):\n+        data = super(AbstractSelectionFieldWrapper, self).getValues()\n+        data.update({\n+            'options': self.field.field_data['options']\n+        })\n+        return data\n+\n+    def check(self, content):\n+        errors = super(AbstractSelectionFieldWrapper, self).check(content)\n+\n+        if content:\n+            if next((op for op in self.getOptions() if op.getId() == content), None) is None:\n+                errors.append(_(\"The option with ID '{}' in the field {} doesn't exist\").format(\n+                    content, self.field.title))\n+\n+        return errors\n+\n+\n+class SelectedFieldOptionWrapper(object):\n+    \"\"\"Wraps a selection field option.\"\"\"\n+\n+    def __init__(self, option):\n+        self.id = option['id']\n+        self.deleted = option['is_deleted']\n+        self.value = option['option']\n+\n+    def __eq__(self, other):\n+        if isinstance(other, SelectedFieldOptionWrapper):\n+            return self.id == other.id\n+        return False\n+\n+    def __hash__(self):\n+        return hash(self.id)\n+\n+    def __str__(self):\n+        return self.value\n+\n+    def getValue(self):\n+        return self.value\n+\n+    def getId(self):\n+        return self.id\n+\n+    def isDeleted(self):\n+        return self.deleted\n+\n+\n+class AbstractFieldManagerAdapter(object):\n+    \"\"\"\n+    Provides an interface that closely resembles the old AbstractFieldMgr.\n+\n+    But actually returns stored ``ContributionField``s.\n+    \"\"\"\n+\n+    def __init__(self, event):\n+        self.event = event\n+\n+    @property\n+    def description_field(self):\n+        return AbstractDescriptionFieldProxy(self.event)\n+\n+    def _notifyModification(self):\n+        pass\n+\n+    def hasField(self, field_id):\n+        if field_id == 'content':\n+            return True\n+\n+        legacy = self.event.contribution_fields.filter_by(legacy_id=field_id)\n+        if legacy.count():\n+            return True\n+        else:\n+            return self.event.contribution_fields.filter_by(id=int(field_id)).count() > 0\n+\n+    def getFieldById(self, field_id):\n+        if field_id == 'content':\n+            field = self.description_field\n+        else:\n+            field = self.event.contribution_fields.filter_by(legacy_id=field_id).first()\n+            if not field:\n+                field = self.event.contribution_fields.filter_by(id=int(field_id)).first()\n+        return AbstractFieldWrapper.wrap(field) if field else None\n+\n+    def getFields(self):\n+        fields = [AbstractFieldWrapper.wrap(field) for field in self.event.contribution_fields]","path":"indico/modules/events/abstracts/legacy.py","position":331,"original_position":331,"commit_id":"d59e627c2e6e655c199a8b8add7b79463bc6df23","original_commit_id":"d59e627c2e6e655c199a8b8add7b79463bc6df23","user":{"login":"ThiefMaster","id":179599,"avatar_url":"https://avatars.githubusercontent.com/u/179599?v=3","gravatar_id":"","url":"https://api.github.com/users/ThiefMaster","html_url":"https://github.com/ThiefMaster","followers_url":"https://api.github.com/users/ThiefMaster/followers","following_url":"https://api.github.com/users/ThiefMaster/following{/other_user}","gists_url":"https://api.github.com/users/ThiefMaster/gists{/gist_id}","starred_url":"https://api.github.com/users/ThiefMaster/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ThiefMaster/subscriptions","organizations_url":"https://api.github.com/users/ThiefMaster/orgs","repos_url":"https://api.github.com/users/ThiefMaster/repos","events_url":"https://api.github.com/users/ThiefMaster/events{/privacy}","received_events_url":"https://api.github.com/users/ThiefMaster/received_events","type":"User","site_admin":false},"body":"`map(AbstractFieldWrapper.wrap, self.event.contribution_fields)`","created_at":"2016-04-05T22:48:24Z","updated_at":"2016-04-05T22:48:24Z","html_url":"https://github.com/indico/indico/pull/2314#discussion_r58626932","pull_request_url":"https://api.github.com/repos/indico/indico/pulls/2314","_links":{"self":{"href":"https://api.github.com/repos/indico/indico/pulls/comments/58626932"},"html":{"href":"https://github.com/indico/indico/pull/2314#discussion_r58626932"},"pull_request":{"href":"https://api.github.com/repos/indico/indico/pulls/2314"}}},"pull_request":{"url":"https://api.github.com/repos/indico/indico/pulls/2314","id":64921745,"html_url":"https://github.com/indico/indico/pull/2314","diff_url":"https://github.com/indico/indico/pull/2314.diff","patch_url":"https://github.com/indico/indico/pull/2314.patch","issue_url":"https://api.github.com/repos/indico/indico/issues/2314","number":2314,"state":"open","locked":false,"title":"Legacy abstract handling","user":{"login":"pferreir","id":2699,"avatar_url":"https://avatars.githubusercontent.com/u/2699?v=3","gravatar_id":"","url":"https://api.github.com/users/pferreir","html_url":"https://github.com/pferreir","followers_url":"https://api.github.com/users/pferreir/followers","following_url":"https://api.github.com/users/pferreir/following{/other_user}","gists_url":"https://api.github.com/users/pferreir/gists{/gist_id}","starred_url":"https://api.github.com/users/pferreir/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pferreir/subscriptions","organizations_url":"https://api.github.com/users/pferreir/orgs","repos_url":"https://api.github.com/users/pferreir/repos","events_url":"https://api.github.com/users/pferreir/events{/privacy}","received_events_url":"https://api.github.com/users/pferreir/received_events","type":"User","site_admin":false},"body":"OK, some minor house cleaning missing, so, I think this is ready for some feedback.\r\nPlease note that I've been pretty gentle with some legacy code and kept some compatibility methods as not to rewrite everything.","created_at":"2016-04-01T10:01:42Z","updated_at":"2016-04-05T22:48:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c558ee56ebb5bf3da0800c1feaeef1507076f46c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/indico/indico/pulls/2314/commits","review_comments_url":"https://api.github.com/repos/indico/indico/pulls/2314/comments","review_comment_url":"https://api.github.com/repos/indico/indico/pulls/comments{/number}","comments_url":"https://api.github.com/repos/indico/indico/issues/2314/comments","statuses_url":"https://api.github.com/repos/indico/indico/statuses/d59e627c2e6e655c199a8b8add7b79463bc6df23","head":{"label":"pferreir:wip/legacy-abstracts","ref":"wip/legacy-abstracts","sha":"d59e627c2e6e655c199a8b8add7b79463bc6df23","user":{"login":"pferreir","id":2699,"avatar_url":"https://avatars.githubusercontent.com/u/2699?v=3","gravatar_id":"","url":"https://api.github.com/users/pferreir","html_url":"https://github.com/pferreir","followers_url":"https://api.github.com/users/pferreir/followers","following_url":"https://api.github.com/users/pferreir/following{/other_user}","gists_url":"https://api.github.com/users/pferreir/gists{/gist_id}","starred_url":"https://api.github.com/users/pferreir/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pferreir/subscriptions","organizations_url":"https://api.github.com/users/pferreir/orgs","repos_url":"https://api.github.com/users/pferreir/repos","events_url":"https://api.github.com/users/pferreir/events{/privacy}","received_events_url":"https://api.github.com/users/pferreir/received_events","type":"User","site_admin":false},"repo":{"id":3867135,"name":"indico","full_name":"pferreir/indico","owner":{"login":"pferreir","id":2699,"avatar_url":"https://avatars.githubusercontent.com/u/2699?v=3","gravatar_id":"","url":"https://api.github.com/users/pferreir","html_url":"https://github.com/pferreir","followers_url":"https://api.github.com/users/pferreir/followers","following_url":"https://api.github.com/users/pferreir/following{/other_user}","gists_url":"https://api.github.com/users/pferreir/gists{/gist_id}","starred_url":"https://api.github.com/users/pferreir/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pferreir/subscriptions","organizations_url":"https://api.github.com/users/pferreir/orgs","repos_url":"https://api.github.com/users/pferreir/repos","events_url":"https://api.github.com/users/pferreir/events{/privacy}","received_events_url":"https://api.github.com/users/pferreir/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pferreir/indico","description":"The Indico Project (mirror)","fork":true,"url":"https://api.github.com/repos/pferreir/indico","forks_url":"https://api.github.com/repos/pferreir/indico/forks","keys_url":"https://api.github.com/repos/pferreir/indico/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pferreir/indico/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pferreir/indico/teams","hooks_url":"https://api.github.com/repos/pferreir/indico/hooks","issue_events_url":"https://api.github.com/repos/pferreir/indico/issues/events{/number}","events_url":"https://api.github.com/repos/pferreir/indico/events","assignees_url":"https://api.github.com/repos/pferreir/indico/assignees{/user}","branches_url":"https://api.github.com/repos/pferreir/indico/branches{/branch}","tags_url":"https://api.github.com/repos/pferreir/indico/tags","blobs_url":"https://api.github.com/repos/pferreir/indico/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pferreir/indico/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pferreir/indico/git/refs{/sha}","trees_url":"https://api.github.com/repos/pferreir/indico/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pferreir/indico/statuses/{sha}","languages_url":"https://api.github.com/repos/pferreir/indico/languages","stargazers_url":"https://api.github.com/repos/pferreir/indico/stargazers","contributors_url":"https://api.github.com/repos/pferreir/indico/contributors","subscribers_url":"https://api.github.com/repos/pferreir/indico/subscribers","subscription_url":"https://api.github.com/repos/pferreir/indico/subscription","commits_url":"https://api.github.com/repos/pferreir/indico/commits{/sha}","git_commits_url":"https://api.github.com/repos/pferreir/indico/git/commits{/sha}","comments_url":"https://api.github.com/repos/pferreir/indico/comments{/number}","issue_comment_url":"https://api.github.com/repos/pferreir/indico/issues/comments{/number}","contents_url":"https://api.github.com/repos/pferreir/indico/contents/{+path}","compare_url":"https://api.github.com/repos/pferreir/indico/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pferreir/indico/merges","archive_url":"https://api.github.com/repos/pferreir/indico/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pferreir/indico/downloads","issues_url":"https://api.github.com/repos/pferreir/indico/issues{/number}","pulls_url":"https://api.github.com/repos/pferreir/indico/pulls{/number}","milestones_url":"https://api.github.com/repos/pferreir/indico/milestones{/number}","notifications_url":"https://api.github.com/repos/pferreir/indico/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pferreir/indico/labels{/name}","releases_url":"https://api.github.com/repos/pferreir/indico/releases{/id}","deployments_url":"https://api.github.com/repos/pferreir/indico/deployments","created_at":"2012-03-29T15:01:36Z","updated_at":"2015-05-04T14:44:08Z","pushed_at":"2016-04-05T09:12:09Z","git_url":"git://github.com/pferreir/indico.git","ssh_url":"git@github.com:pferreir/indico.git","clone_url":"https://github.com/pferreir/indico.git","svn_url":"https://github.com/pferreir/indico","homepage":"http://indico-software.org","size":186935,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"indico:devel","ref":"devel","sha":"dd75ea37726c1e809ada784da5438b9bf4a42b56","user":{"login":"indico","id":715236,"avatar_url":"https://avatars.githubusercontent.com/u/715236?v=3","gravatar_id":"","url":"https://api.github.com/users/indico","html_url":"https://github.com/indico","followers_url":"https://api.github.com/users/indico/followers","following_url":"https://api.github.com/users/indico/following{/other_user}","gists_url":"https://api.github.com/users/indico/gists{/gist_id}","starred_url":"https://api.github.com/users/indico/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/indico/subscriptions","organizations_url":"https://api.github.com/users/indico/orgs","repos_url":"https://api.github.com/users/indico/repos","events_url":"https://api.github.com/users/indico/events{/privacy}","received_events_url":"https://api.github.com/users/indico/received_events","type":"Organization","site_admin":false},"repo":{"id":2113067,"name":"indico","full_name":"indico/indico","owner":{"login":"indico","id":715236,"avatar_url":"https://avatars.githubusercontent.com/u/715236?v=3","gravatar_id":"","url":"https://api.github.com/users/indico","html_url":"https://github.com/indico","followers_url":"https://api.github.com/users/indico/followers","following_url":"https://api.github.com/users/indico/following{/other_user}","gists_url":"https://api.github.com/users/indico/gists{/gist_id}","starred_url":"https://api.github.com/users/indico/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/indico/subscriptions","organizations_url":"https://api.github.com/users/indico/orgs","repos_url":"https://api.github.com/users/indico/repos","events_url":"https://api.github.com/users/indico/events{/privacy}","received_events_url":"https://api.github.com/users/indico/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/indico/indico","description":"The Indico Project","fork":false,"url":"https://api.github.com/repos/indico/indico","forks_url":"https://api.github.com/repos/indico/indico/forks","keys_url":"https://api.github.com/repos/indico/indico/keys{/key_id}","collaborators_url":"https://api.github.com/repos/indico/indico/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/indico/indico/teams","hooks_url":"https://api.github.com/repos/indico/indico/hooks","issue_events_url":"https://api.github.com/repos/indico/indico/issues/events{/number}","events_url":"https://api.github.com/repos/indico/indico/events","assignees_url":"https://api.github.com/repos/indico/indico/assignees{/user}","branches_url":"https://api.github.com/repos/indico/indico/branches{/branch}","tags_url":"https://api.github.com/repos/indico/indico/tags","blobs_url":"https://api.github.com/repos/indico/indico/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/indico/indico/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/indico/indico/git/refs{/sha}","trees_url":"https://api.github.com/repos/indico/indico/git/trees{/sha}","statuses_url":"https://api.github.com/repos/indico/indico/statuses/{sha}","languages_url":"https://api.github.com/repos/indico/indico/languages","stargazers_url":"https://api.github.com/repos/indico/indico/stargazers","contributors_url":"https://api.github.com/repos/indico/indico/contributors","subscribers_url":"https://api.github.com/repos/indico/indico/subscribers","subscription_url":"https://api.github.com/repos/indico/indico/subscription","commits_url":"https://api.github.com/repos/indico/indico/commits{/sha}","git_commits_url":"https://api.github.com/repos/indico/indico/git/commits{/sha}","comments_url":"https://api.github.com/repos/indico/indico/comments{/number}","issue_comment_url":"https://api.github.com/repos/indico/indico/issues/comments{/number}","contents_url":"https://api.github.com/repos/indico/indico/contents/{+path}","compare_url":"https://api.github.com/repos/indico/indico/compare/{base}...{head}","merges_url":"https://api.github.com/repos/indico/indico/merges","archive_url":"https://api.github.com/repos/indico/indico/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/indico/indico/downloads","issues_url":"https://api.github.com/repos/indico/indico/issues{/number}","pulls_url":"https://api.github.com/repos/indico/indico/pulls{/number}","milestones_url":"https://api.github.com/repos/indico/indico/milestones{/number}","notifications_url":"https://api.github.com/repos/indico/indico/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/indico/indico/labels{/name}","releases_url":"https://api.github.com/repos/indico/indico/releases{/id}","deployments_url":"https://api.github.com/repos/indico/indico/deployments","created_at":"2011-07-27T13:56:30Z","updated_at":"2016-04-04T03:34:41Z","pushed_at":"2016-04-05T16:16:36Z","git_url":"git://github.com/indico/indico.git","ssh_url":"git@github.com:indico/indico.git","clone_url":"https://github.com/indico/indico.git","svn_url":"https://github.com/indico/indico","homepage":"http://indico-software.org","size":195217,"stargazers_count":135,"watchers_count":135,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":69,"mirror_url":null,"open_issues_count":592,"forks":69,"open_issues":592,"watchers":135,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/indico/indico/pulls/2314"},"html":{"href":"https://github.com/indico/indico/pull/2314"},"issue":{"href":"https://api.github.com/repos/indico/indico/issues/2314"},"comments":{"href":"https://api.github.com/repos/indico/indico/issues/2314/comments"},"review_comments":{"href":"https://api.github.com/repos/indico/indico/pulls/2314/comments"},"review_comment":{"href":"https://api.github.com/repos/indico/indico/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/indico/indico/pulls/2314/commits"},"statuses":{"href":"https://api.github.com/repos/indico/indico/statuses/d59e627c2e6e655c199a8b8add7b79463bc6df23"}}}}