{"action":"created","comment":{"url":"https://api.github.com/repos/scrapinghub/portia/pulls/comments/71687728","id":71687728,"diff_hunk":"@@ -1,144 +1,230 @@\n-from __future__ import absolute_import\n-import six\n+from collections import OrderedDict\n import json\n \n-from .models import (ProjectSchema, SchemaSchema, FieldSchema, ExtractorSchema,\n-                     SpiderSchema)\n-from .schemas import _read_schemas\n-from .utils import _read_extractors, BaseApiResponse\n+from django.utils.functional import cached_property\n+from six import itervalues, string_types\n+\n+from .models import ProjectSchema as OldProjectSchema\n+from .response import JsonApiResource, ProjectDownloadResponse\n+from .route import JsonApiRoute, ListModelMixin, RetrieveModelMixin\n+from .serializers import ProjectSchema\n from ..errors import BadRequest, BaseError, NotFound\n-from ..utils.projects import ctx, init_project\n+from ..orm.models import Project\n+\n NOT_AVAILABLE_ERROR = 'This feature is not available for your project.'\n \n \n-def list_projects(manager, attributes=None):\n-    \"\"\"List all available project for the current user\"\"\"\n-    projects = []\n-    for project in manager.list_projects():\n-        if isinstance(project, six.string_types):\n-            project = {'id': project, 'name': project}\n-        elif isinstance(project, dict):\n-            if not project.get('id'):\n-                project['id'] = project['name']\n-        projects.append(project)\n-    return ProjectSchema(many=True).dump(projects).data\n-\n-\n-@init_project\n-def get_project(manager, attributes=None):\n-    \"\"\"Get current project including schemas and extractors if required\"\"\"\n-    project_id = manager.project_name\n-    name = _get_project_name(manager.pm, project_id)\n-    project = {'name': name, 'id': project_id}\n-    schemas = [{'id': s} for s in _read_schemas(manager)]\n-    spiders = [{'id': s} for s in manager.list_spiders()]\n-    extractors = [{'id': s} for s in _read_extractors(manager)]\n-    project['schemas'] = schemas\n-    project['spiders'] = spiders\n-    project['extractors'] = extractors\n-    data = ProjectSchema(context=ctx(manager)).dump(project).data\n-    return data\n-\n-\n-def create_project(manager, attributes):\n-    \"\"\"Create a new project from the provided attributes\"\"\"\n-    attributes = _check_project_attributes(manager, attributes)\n-    manager.create_project(attributes['name'])\n-    return ProjectSchema().dump({'name': attributes['name']}).data\n-\n-\n-def update_project(manager, project_id, attributes):\n-    \"\"\"Update an exiting project with the provided attributes\"\"\"\n-    attributes = _check_project_attributes(manager, attributes)\n-    if project_id != attributes['name']:\n-        manager.rename_project(project_id, attributes['name'])\n-    return ProjectSchema().dump({'name': attributes['name']}).data\n-\n-\n-def delete_project(manager, project_id, attributes=None):\n-    \"\"\"Delete the request project\"\"\"\n-    manager.remove_project(project_id)\n-    return ProjectSchema.empty_data()\n-\n-\n-def status(manager, attributes=None):\n-    if not hasattr(manager.pm, '_changed_files'):\n-        raise NotFound(NOT_AVAILABLE_ERROR)\n-    project_id = manager.project_name\n-    data = get_project(manager)\n-    data['meta'] = {\n-        'changes': [\n-            {'type': type_, 'path': path, 'old_path': old_path}\n-            for type_, path, old_path in manager.pm._changed_files(project_id)]\n-    }\n-    return data\n-\n-\n-def merge(manager, attributes=None):\n-    if not hasattr(manager.pm, 'publish_project'):\n-        raise NotFound(NOT_AVAILABLE_ERROR)\n-    project_id = manager.project_name\n-    if not status(manager):\n-        raise BadRequest('The project is up to date')\n-    publish_status = json.loads(\n-        manager.pm.publish_project(project_id, attributes.get('force', False)))\n-    if publish_status['status'] == 'conflict':\n-        raise BaseError(409, 'A conflict has occurred in this project',\n-                        'You must resolve the conflict for the project to be'\n-                        ' successfully published')\n-    data = get_project(manager)\n-    data['meta'] = manager.pm._schedule_data(project_id=project_id)\n-    return data\n-\n-\n-def reset(manager, attributes=None):\n-    if not hasattr(manager.pm, 'discard_changes'):\n-        raise NotFound(NOT_AVAILABLE_ERROR)\n-    project_id = manager.project_name\n-    if not status(manager):\n-        raise BadRequest('There are no changes to discard')\n-    manager.pm.discard_changes(project_id)\n-    return get_project(manager)\n-\n-\n-class ProjectsManagerFileResponse(BaseApiResponse):\n-    def __init__(self, data, command, project_manager):\n-        super(ProjectsManagerFileResponse, self).__init__(data)\n-        self.command = command\n-        self.project_manager = project_manager\n-\n-    def format_response(self, request):\n-        return self.project_manager._render_file(request, self.command,\n-                                                 self.data)\n-\n-\n-def download(manager, spider_id=None, attributes=None):\n-    project_id = manager.project_name\n-    spider_ids = [spider_id] if spider_id is not None else '*'\n-    command = {\n-        'cmd': 'download',\n-        'args': [project_id, spider_ids]\n-    }\n-    fmt = attributes['arguments'].get('format', ['spec'])[0]\n-    file_content = manager.pm.download_project(project_id, spider_ids, fmt=fmt)\n-    return ProjectsManagerFileResponse(file_content, command, manager.pm)\n+class ProjectDownloadMixin(object):\n+    @classmethod\n+    def get_resources(cls):\n+        for resouce in super(ProjectDownloadMixin, cls).get_resources():\n+            yield resouce\n+        yield 'get', cls.download_path\n+\n+    def get_handler(self):\n+        if self.route_path == self.download_path:\n+            return self.download\n+        return super(ProjectDownloadMixin, self).get_handler()\n+\n+    def download(self):\n+        project_manager = self.project_manager\n+        project_id = self.args.get('project_id')\n+        spider_id = self.args.get('spider_id', None)\n+        spider_ids = [spider_id] if spider_id is not None else '*'\n+        fmt = self.query.get('format', ['spec'])[0]\n+        return ProjectDownloadResponse(\n+            project_id, spider_ids, fmt, project_manager)\n+\n+\n+class ProjectDataMixin(object):","path":"slyd/slyd/resources/projects.py","position":156,"original_position":157,"commit_id":"cfd3454e42a3e1b10b9c37c1b440214b90b255bd","original_commit_id":"db9dfb14a258b2fa5eeb221dd8f714853ee8e3d5","user":{"login":"michalmo","id":9825131,"avatar_url":"https://avatars.githubusercontent.com/u/9825131?v=3","gravatar_id":"","url":"https://api.github.com/users/michalmo","html_url":"https://github.com/michalmo","followers_url":"https://api.github.com/users/michalmo/followers","following_url":"https://api.github.com/users/michalmo/following{/other_user}","gists_url":"https://api.github.com/users/michalmo/gists{/gist_id}","starred_url":"https://api.github.com/users/michalmo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michalmo/subscriptions","organizations_url":"https://api.github.com/users/michalmo/orgs","repos_url":"https://api.github.com/users/michalmo/repos","events_url":"https://api.github.com/users/michalmo/events{/privacy}","received_events_url":"https://api.github.com/users/michalmo/received_events","type":"User","site_admin":false},"body":"that would change the interface by which the routes are initialized in `slyd.api.APIResource`, and i'm not convinced that making that more complex to simplify this makes sense.\r\non the other hand making a `BaseProjectRoute` used by both `ProjectRoute` and `SpiderRoute` that includes the property or sets the value in `__init__` is fundamentally no different than this mixin (the subclass case just changes the bases of the classes but everything else remains the same).\r\nor did you have some other refactoring in mind?","created_at":"2016-07-21T11:20:48Z","updated_at":"2016-07-21T11:20:48Z","html_url":"https://github.com/scrapinghub/portia/pull/524#discussion_r71687728","pull_request_url":"https://api.github.com/repos/scrapinghub/portia/pulls/524","_links":{"self":{"href":"https://api.github.com/repos/scrapinghub/portia/pulls/comments/71687728"},"html":{"href":"https://github.com/scrapinghub/portia/pull/524#discussion_r71687728"},"pull_request":{"href":"https://api.github.com/repos/scrapinghub/portia/pulls/524"}}},"pull_request":{"url":"https://api.github.com/repos/scrapinghub/portia/pulls/524","id":76396681,"html_url":"https://github.com/scrapinghub/portia/pull/524","diff_url":"https://github.com/scrapinghub/portia/pull/524.diff","patch_url":"https://github.com/scrapinghub/portia/pull/524.patch","issue_url":"https://api.github.com/repos/scrapinghub/portia/issues/524","number":524,"state":"open","locked":false,"title":"[WIP] PORTIA-494: JSON API endpoints for ORM","user":{"login":"michalmo","id":9825131,"avatar_url":"https://avatars.githubusercontent.com/u/9825131?v=3","gravatar_id":"","url":"https://api.github.com/users/michalmo","html_url":"https://github.com/michalmo","followers_url":"https://api.github.com/users/michalmo/followers","following_url":"https://api.github.com/users/michalmo/following{/other_user}","gists_url":"https://api.github.com/users/michalmo/gists{/gist_id}","starred_url":"https://api.github.com/users/michalmo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michalmo/subscriptions","organizations_url":"https://api.github.com/users/michalmo/orgs","repos_url":"https://api.github.com/users/michalmo/repos","events_url":"https://api.github.com/users/michalmo/events{/privacy}","received_events_url":"https://api.github.com/users/michalmo/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-07-06T08:40:47Z","updated_at":"2016-07-21T11:20:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"377a739b787d36ac9adb02604d2caa8aa7d8f3fe","assignee":{"login":"ruairif","id":6472969,"avatar_url":"https://avatars.githubusercontent.com/u/6472969?v=3","gravatar_id":"","url":"https://api.github.com/users/ruairif","html_url":"https://github.com/ruairif","followers_url":"https://api.github.com/users/ruairif/followers","following_url":"https://api.github.com/users/ruairif/following{/other_user}","gists_url":"https://api.github.com/users/ruairif/gists{/gist_id}","starred_url":"https://api.github.com/users/ruairif/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ruairif/subscriptions","organizations_url":"https://api.github.com/users/ruairif/orgs","repos_url":"https://api.github.com/users/ruairif/repos","events_url":"https://api.github.com/users/ruairif/events{/privacy}","received_events_url":"https://api.github.com/users/ruairif/received_events","type":"User","site_admin":false},"assignees":[{"login":"ruairif","id":6472969,"avatar_url":"https://avatars.githubusercontent.com/u/6472969?v=3","gravatar_id":"","url":"https://api.github.com/users/ruairif","html_url":"https://github.com/ruairif","followers_url":"https://api.github.com/users/ruairif/followers","following_url":"https://api.github.com/users/ruairif/following{/other_user}","gists_url":"https://api.github.com/users/ruairif/gists{/gist_id}","starred_url":"https://api.github.com/users/ruairif/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ruairif/subscriptions","organizations_url":"https://api.github.com/users/ruairif/orgs","repos_url":"https://api.github.com/users/ruairif/repos","events_url":"https://api.github.com/users/ruairif/events{/privacy}","received_events_url":"https://api.github.com/users/ruairif/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/scrapinghub/portia/pulls/524/commits","review_comments_url":"https://api.github.com/repos/scrapinghub/portia/pulls/524/comments","review_comment_url":"https://api.github.com/repos/scrapinghub/portia/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scrapinghub/portia/issues/524/comments","statuses_url":"https://api.github.com/repos/scrapinghub/portia/statuses/cfd3454e42a3e1b10b9c37c1b440214b90b255bd","head":{"label":"scrapinghub:portia-orm-jsonapi","ref":"portia-orm-jsonapi","sha":"cfd3454e42a3e1b10b9c37c1b440214b90b255bd","user":{"login":"scrapinghub","id":699596,"avatar_url":"https://avatars.githubusercontent.com/u/699596?v=3","gravatar_id":"","url":"https://api.github.com/users/scrapinghub","html_url":"https://github.com/scrapinghub","followers_url":"https://api.github.com/users/scrapinghub/followers","following_url":"https://api.github.com/users/scrapinghub/following{/other_user}","gists_url":"https://api.github.com/users/scrapinghub/gists{/gist_id}","starred_url":"https://api.github.com/users/scrapinghub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scrapinghub/subscriptions","organizations_url":"https://api.github.com/users/scrapinghub/orgs","repos_url":"https://api.github.com/users/scrapinghub/repos","events_url":"https://api.github.com/users/scrapinghub/events{/privacy}","received_events_url":"https://api.github.com/users/scrapinghub/received_events","type":"Organization","site_admin":false},"repo":{"id":17982061,"name":"portia","full_name":"scrapinghub/portia","owner":{"login":"scrapinghub","id":699596,"avatar_url":"https://avatars.githubusercontent.com/u/699596?v=3","gravatar_id":"","url":"https://api.github.com/users/scrapinghub","html_url":"https://github.com/scrapinghub","followers_url":"https://api.github.com/users/scrapinghub/followers","following_url":"https://api.github.com/users/scrapinghub/following{/other_user}","gists_url":"https://api.github.com/users/scrapinghub/gists{/gist_id}","starred_url":"https://api.github.com/users/scrapinghub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scrapinghub/subscriptions","organizations_url":"https://api.github.com/users/scrapinghub/orgs","repos_url":"https://api.github.com/users/scrapinghub/repos","events_url":"https://api.github.com/users/scrapinghub/events{/privacy}","received_events_url":"https://api.github.com/users/scrapinghub/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scrapinghub/portia","description":"Visual scraping for Scrapy","fork":false,"url":"https://api.github.com/repos/scrapinghub/portia","forks_url":"https://api.github.com/repos/scrapinghub/portia/forks","keys_url":"https://api.github.com/repos/scrapinghub/portia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scrapinghub/portia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scrapinghub/portia/teams","hooks_url":"https://api.github.com/repos/scrapinghub/portia/hooks","issue_events_url":"https://api.github.com/repos/scrapinghub/portia/issues/events{/number}","events_url":"https://api.github.com/repos/scrapinghub/portia/events","assignees_url":"https://api.github.com/repos/scrapinghub/portia/assignees{/user}","branches_url":"https://api.github.com/repos/scrapinghub/portia/branches{/branch}","tags_url":"https://api.github.com/repos/scrapinghub/portia/tags","blobs_url":"https://api.github.com/repos/scrapinghub/portia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scrapinghub/portia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scrapinghub/portia/git/refs{/sha}","trees_url":"https://api.github.com/repos/scrapinghub/portia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scrapinghub/portia/statuses/{sha}","languages_url":"https://api.github.com/repos/scrapinghub/portia/languages","stargazers_url":"https://api.github.com/repos/scrapinghub/portia/stargazers","contributors_url":"https://api.github.com/repos/scrapinghub/portia/contributors","subscribers_url":"https://api.github.com/repos/scrapinghub/portia/subscribers","subscription_url":"https://api.github.com/repos/scrapinghub/portia/subscription","commits_url":"https://api.github.com/repos/scrapinghub/portia/commits{/sha}","git_commits_url":"https://api.github.com/repos/scrapinghub/portia/git/commits{/sha}","comments_url":"https://api.github.com/repos/scrapinghub/portia/comments{/number}","issue_comment_url":"https://api.github.com/repos/scrapinghub/portia/issues/comments{/number}","contents_url":"https://api.github.com/repos/scrapinghub/portia/contents/{+path}","compare_url":"https://api.github.com/repos/scrapinghub/portia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scrapinghub/portia/merges","archive_url":"https://api.github.com/repos/scrapinghub/portia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scrapinghub/portia/downloads","issues_url":"https://api.github.com/repos/scrapinghub/portia/issues{/number}","pulls_url":"https://api.github.com/repos/scrapinghub/portia/pulls{/number}","milestones_url":"https://api.github.com/repos/scrapinghub/portia/milestones{/number}","notifications_url":"https://api.github.com/repos/scrapinghub/portia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scrapinghub/portia/labels{/name}","releases_url":"https://api.github.com/repos/scrapinghub/portia/releases{/id}","deployments_url":"https://api.github.com/repos/scrapinghub/portia/deployments","created_at":"2014-03-21T14:24:31Z","updated_at":"2016-07-21T04:00:43Z","pushed_at":"2016-07-21T11:03:48Z","git_url":"git://github.com/scrapinghub/portia.git","ssh_url":"git@github.com:scrapinghub/portia.git","clone_url":"https://github.com/scrapinghub/portia.git","svn_url":"https://github.com/scrapinghub/portia","homepage":null,"size":26289,"stargazers_count":4402,"watchers_count":4402,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":643,"mirror_url":null,"open_issues_count":85,"forks":643,"open_issues":85,"watchers":4402,"default_branch":"master"}},"base":{"label":"scrapinghub:nui-develop","ref":"nui-develop","sha":"d5fe1970ce9e7294764d53cce2a7a1977c8d7d21","user":{"login":"scrapinghub","id":699596,"avatar_url":"https://avatars.githubusercontent.com/u/699596?v=3","gravatar_id":"","url":"https://api.github.com/users/scrapinghub","html_url":"https://github.com/scrapinghub","followers_url":"https://api.github.com/users/scrapinghub/followers","following_url":"https://api.github.com/users/scrapinghub/following{/other_user}","gists_url":"https://api.github.com/users/scrapinghub/gists{/gist_id}","starred_url":"https://api.github.com/users/scrapinghub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scrapinghub/subscriptions","organizations_url":"https://api.github.com/users/scrapinghub/orgs","repos_url":"https://api.github.com/users/scrapinghub/repos","events_url":"https://api.github.com/users/scrapinghub/events{/privacy}","received_events_url":"https://api.github.com/users/scrapinghub/received_events","type":"Organization","site_admin":false},"repo":{"id":17982061,"name":"portia","full_name":"scrapinghub/portia","owner":{"login":"scrapinghub","id":699596,"avatar_url":"https://avatars.githubusercontent.com/u/699596?v=3","gravatar_id":"","url":"https://api.github.com/users/scrapinghub","html_url":"https://github.com/scrapinghub","followers_url":"https://api.github.com/users/scrapinghub/followers","following_url":"https://api.github.com/users/scrapinghub/following{/other_user}","gists_url":"https://api.github.com/users/scrapinghub/gists{/gist_id}","starred_url":"https://api.github.com/users/scrapinghub/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scrapinghub/subscriptions","organizations_url":"https://api.github.com/users/scrapinghub/orgs","repos_url":"https://api.github.com/users/scrapinghub/repos","events_url":"https://api.github.com/users/scrapinghub/events{/privacy}","received_events_url":"https://api.github.com/users/scrapinghub/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scrapinghub/portia","description":"Visual scraping for Scrapy","fork":false,"url":"https://api.github.com/repos/scrapinghub/portia","forks_url":"https://api.github.com/repos/scrapinghub/portia/forks","keys_url":"https://api.github.com/repos/scrapinghub/portia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scrapinghub/portia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scrapinghub/portia/teams","hooks_url":"https://api.github.com/repos/scrapinghub/portia/hooks","issue_events_url":"https://api.github.com/repos/scrapinghub/portia/issues/events{/number}","events_url":"https://api.github.com/repos/scrapinghub/portia/events","assignees_url":"https://api.github.com/repos/scrapinghub/portia/assignees{/user}","branches_url":"https://api.github.com/repos/scrapinghub/portia/branches{/branch}","tags_url":"https://api.github.com/repos/scrapinghub/portia/tags","blobs_url":"https://api.github.com/repos/scrapinghub/portia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scrapinghub/portia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scrapinghub/portia/git/refs{/sha}","trees_url":"https://api.github.com/repos/scrapinghub/portia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scrapinghub/portia/statuses/{sha}","languages_url":"https://api.github.com/repos/scrapinghub/portia/languages","stargazers_url":"https://api.github.com/repos/scrapinghub/portia/stargazers","contributors_url":"https://api.github.com/repos/scrapinghub/portia/contributors","subscribers_url":"https://api.github.com/repos/scrapinghub/portia/subscribers","subscription_url":"https://api.github.com/repos/scrapinghub/portia/subscription","commits_url":"https://api.github.com/repos/scrapinghub/portia/commits{/sha}","git_commits_url":"https://api.github.com/repos/scrapinghub/portia/git/commits{/sha}","comments_url":"https://api.github.com/repos/scrapinghub/portia/comments{/number}","issue_comment_url":"https://api.github.com/repos/scrapinghub/portia/issues/comments{/number}","contents_url":"https://api.github.com/repos/scrapinghub/portia/contents/{+path}","compare_url":"https://api.github.com/repos/scrapinghub/portia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scrapinghub/portia/merges","archive_url":"https://api.github.com/repos/scrapinghub/portia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scrapinghub/portia/downloads","issues_url":"https://api.github.com/repos/scrapinghub/portia/issues{/number}","pulls_url":"https://api.github.com/repos/scrapinghub/portia/pulls{/number}","milestones_url":"https://api.github.com/repos/scrapinghub/portia/milestones{/number}","notifications_url":"https://api.github.com/repos/scrapinghub/portia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scrapinghub/portia/labels{/name}","releases_url":"https://api.github.com/repos/scrapinghub/portia/releases{/id}","deployments_url":"https://api.github.com/repos/scrapinghub/portia/deployments","created_at":"2014-03-21T14:24:31Z","updated_at":"2016-07-21T04:00:43Z","pushed_at":"2016-07-21T11:03:48Z","git_url":"git://github.com/scrapinghub/portia.git","ssh_url":"git@github.com:scrapinghub/portia.git","clone_url":"https://github.com/scrapinghub/portia.git","svn_url":"https://github.com/scrapinghub/portia","homepage":null,"size":26289,"stargazers_count":4402,"watchers_count":4402,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":643,"mirror_url":null,"open_issues_count":85,"forks":643,"open_issues":85,"watchers":4402,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scrapinghub/portia/pulls/524"},"html":{"href":"https://github.com/scrapinghub/portia/pull/524"},"issue":{"href":"https://api.github.com/repos/scrapinghub/portia/issues/524"},"comments":{"href":"https://api.github.com/repos/scrapinghub/portia/issues/524/comments"},"review_comments":{"href":"https://api.github.com/repos/scrapinghub/portia/pulls/524/comments"},"review_comment":{"href":"https://api.github.com/repos/scrapinghub/portia/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scrapinghub/portia/pulls/524/commits"},"statuses":{"href":"https://api.github.com/repos/scrapinghub/portia/statuses/cfd3454e42a3e1b10b9c37c1b440214b90b255bd"}}}}
{"actor":{"display_login":"michalmo"}}