{"action":"created","comment":{"url":"https://api.github.com/repos/airbnb/caravel/pulls/comments/73977410","id":73977410,"diff_hunk":"@@ -0,0 +1,219 @@\n+import celery\n+from caravel import models, app, utils\n+import datetime\n+import logging\n+from sqlalchemy import create_engine, select, text\n+from sqlalchemy.orm import scoped_session, sessionmaker\n+from sqlalchemy.sql.expression import TextAsFrom\n+import sqlparse\n+import pandas as pd\n+\n+celery_app = celery.Celery(config_source=app.config.get('CELERY_CONFIG'))\n+\n+\n+def init_query(database_id, sql, user_id, start_time, tmp_table_name):\n+    \"\"\"Initializes the models.Query object.\n+\n+    :param database_id: integer\n+    :param sql: sql query that will be executed\n+    :param user_id: integer\n+    :param start_time: Datetime, time when query started.\n+    :param tmp_table_name: table name for the CTA\n+    :return: models.Query\n+    \"\"\"\n+\n+    # TODO(bkyryliuk): provide a way for the user to name the query.\n+    # TODO(bkyryliuk): run explain query to derive the tables and fill in the\n+    #                  table_ids\n+    # TODO(bkyryliuk): check the user permissions\n+    limit = app.config.get('SQL_MAX_ROW', None)\n+    if not tmp_table_name:\n+        tmp_table_name = 'tmp.{}_table_{}'.format(user_id, start_time)\n+    query = models.Query(\n+        user_id=user_id,\n+        database_id=database_id,\n+        limit=limit,\n+        name='{}'.format(start_time),\n+        sql=sql,\n+        start_time=start_time,\n+        tmp_table_name=tmp_table_name,\n+        status=models.QueryStatus.IN_PROGRESS\n+    )\n+    return query\n+\n+\n+def is_query_select(sql):\n+    try:\n+        return sqlparse.parse(sql)[0].get_type() == 'SELECT'\n+    # Capture sqlparse exceptions, worker shouldn't fail here.\n+    except Exception:\n+        # TODO(bkyryliuk): add logging here.\n+        return False\n+\n+\n+# if sqlparse provides the stream of tokens but don't provide the API\n+# to access the table names, more on it:\n+# https://groups.google.com/forum/#!topic/sqlparse/sL2aAi6dSJU\n+# https://github.com/andialbrecht/sqlparse/blob/master/examples/\n+# extract_table_names.py\n+#\n+# Another approach would be to run the EXPLAIN on the sql statement:\n+# https://prestodb.io/docs/current/sql/explain.html\n+# https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Explain\n+def get_tables():\n+    \"\"\"Retrieves the query names from the query.\"\"\"\n+    # TODO(bkyryliuk): implement parsing the sql statement.\n+    pass\n+\n+\n+def add_limit_to_the_query(sql, limit, eng):\n+    # Treat as single sql statement in case of failure.\n+    sql_statements = [sql]\n+    try:\n+        sql_statements = [s for s in sqlparse.split(sql) if s]\n+    except Exception as e:\n+        logging.info(\n+            \"Statement \" + sql + \"failed to be transformed to have the limit \"\n+            \"with the exception\" + e.message)\n+        return sql\n+    if len(sql_statements) == 1 and is_query_select(sql):\n+        qry = select('*').select_from(\n+            TextAsFrom(text(sql_statements[0]), ['*']).alias(\n+                'inner_qry')).limit(limit)\n+        sql_statement = str(qry.compile(\n+            eng, compile_kwargs={\"literal_binds\": True}))\n+        return sql_statement\n+    return sql\n+\n+\n+# create table works only for the single statement.\n+def create_table_as(sql, table_name, override=False):\n+    \"\"\"Reformats the query into the create table as query.\n+\n+    Works only for the single select SQL statements, in all other cases\n+    the sql query is not modified.\n+    :param sql: string, sql query that will be executed\n+    :param table_name: string, will contain the results of the query execution\n+    :param override, boolean, table table_name will be dropped if true\n+    :return: string, create table as query\n+    \"\"\"\n+    # TODO(bkyryliuk): drop table if allowed, check the namespace and\n+    #                  the permissions.\n+    # Treat as single sql statement in case of failure.\n+    sql_statements = [sql]\n+    try:\n+        # Filter out empty statements.\n+        sql_statements = [s for s in sqlparse.split(sql) if s]\n+    except Exception as e:\n+        logging.info(\n+            \"Statement \" + sql + \"failed to be transformed as create table as \"\n+            \"with the exception\" + e.message)\n+        return sql\n+    if len(sql_statements) == 1 and is_query_select(sql):\n+        updated_sql = ''\n+        # TODO(bkyryliuk): use sqlalchemy statements for the\n+        #  the drop and create operations.\n+        if override:\n+            updated_sql = 'DROP TABLE IF EXISTS {};\\n'.format(table_name)\n+        updated_sql += \"CREATE TABLE %s AS %s\" % (\n+            table_name, sql_statements[0])\n+        return updated_sql\n+    return sql\n+\n+\n+def get_session():\n+    \"\"\"Creates new SQLAlchemy scoped_session.\"\"\"\n+    engine = create_engine(\n+        app.config.get('SQLALCHEMY_DATABASE_URI'), convert_unicode=True)\n+    return scoped_session(sessionmaker(\n+        autocommit=False, autoflush=False, bind=engine))\n+\n+\n+@celery_app.task\n+def get_sql_results(database_id, sql, user_id, tmp_table_name=\"\"):\n+    \"\"\"Executes the sql query returns the results.\n+\n+    :param database_id: integer\n+    :param sql: string, query that will be executed\n+    :param user_id: integer\n+    :return: dataframe, query result\n+    \"\"\"\n+    # Create a separate session, reusing the db.session leads to the\n+    # concurrency issues.\n+    session = get_session()\n+    try:\n+        db_to_query = (\n+            session.query(models.Database).filter_by(id=database_id).first()\n+        )\n+    except Exception as e:\n+        return {\n+            'error': utils.error_msg_from_exception(e),\n+            'success': False\n+        }\n+    if not db_to_query:\n+        return {\n+            'error': \"Database with id {0} is missing.\".format(database_id),\n+            'success': False\n+        }\n+\n+    query = init_query(\n+        database_id, sql, user_id, datetime.datetime.now(), tmp_table_name)\n+    session.add(query)\n+    session.commit()\n+    query_result = get_sql_results_as_dict(\n+        sql, db_to_query, query.tmp_table_name)\n+    query.end_time = datetime.datetime.now()\n+    if query_result['success']:\n+        query.status = models.QueryStatus.FINISHED\n+        # TODO(bkyryliuk): fill in query.tmp_table_name\n+    else:\n+        query.status = models.QueryStatus.FAILED\n+    session.commit()\n+    # TODO(bkyryliuk): return the tmp table  / query_id\n+    return query_result\n+\n+\n+# TODO(bkyryliuk): merge the changes made in the carapal first\n+#                   before merging this PR.\n+def get_sql_results_as_dict(sql, db_to_query, tmp_table_name):\n+    \"\"\"Get the SQL query results from the give session and db connection.\n+\n+    :param sql: string, query that will be executed\n+    :param db_to_query: models.Database to query\n+    :param tmp_table_name: name of the table for CTA\n+    :return: (dataframe, boolean), results and the status\n+    \"\"\"\n+    try:","path":"caravel/tasks.py","position":186,"original_position":186,"commit_id":"7ff31bb61212da8ead0117d3df4c0b0aef9f2233","original_commit_id":"7ff31bb61212da8ead0117d3df4c0b0aef9f2233","user":{"login":"mistercrunch","id":487433,"avatar_url":"https://avatars.githubusercontent.com/u/487433?v=3","gravatar_id":"","url":"https://api.github.com/users/mistercrunch","html_url":"https://github.com/mistercrunch","followers_url":"https://api.github.com/users/mistercrunch/followers","following_url":"https://api.github.com/users/mistercrunch/following{/other_user}","gists_url":"https://api.github.com/users/mistercrunch/gists{/gist_id}","starred_url":"https://api.github.com/users/mistercrunch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mistercrunch/subscriptions","organizations_url":"https://api.github.com/users/mistercrunch/orgs","repos_url":"https://api.github.com/users/mistercrunch/repos","events_url":"https://api.github.com/users/mistercrunch/events{/privacy}","received_events_url":"https://api.github.com/users/mistercrunch/received_events","type":"User","site_admin":false},"body":"Large try blocks with generic exception handling are generally bad.","created_at":"2016-08-08T23:51:38Z","updated_at":"2016-08-08T23:51:38Z","html_url":"https://github.com/airbnb/caravel/pull/759#discussion_r73977410","pull_request_url":"https://api.github.com/repos/airbnb/caravel/pulls/759","_links":{"self":{"href":"https://api.github.com/repos/airbnb/caravel/pulls/comments/73977410"},"html":{"href":"https://github.com/airbnb/caravel/pull/759#discussion_r73977410"},"pull_request":{"href":"https://api.github.com/repos/airbnb/caravel/pulls/759"}}},"pull_request":{"url":"https://api.github.com/repos/airbnb/caravel/pulls/759","id":77361494,"html_url":"https://github.com/airbnb/caravel/pull/759","diff_url":"https://github.com/airbnb/caravel/pull/759.diff","patch_url":"https://github.com/airbnb/caravel/pull/759.patch","issue_url":"https://api.github.com/repos/airbnb/caravel/issues/759","number":759,"state":"open","locked":false,"title":"Skeleton for remote query execution using celery.","user":{"login":"bkyryliuk","id":5727938,"avatar_url":"https://avatars.githubusercontent.com/u/5727938?v=3","gravatar_id":"","url":"https://api.github.com/users/bkyryliuk","html_url":"https://github.com/bkyryliuk","followers_url":"https://api.github.com/users/bkyryliuk/followers","following_url":"https://api.github.com/users/bkyryliuk/following{/other_user}","gists_url":"https://api.github.com/users/bkyryliuk/gists{/gist_id}","starred_url":"https://api.github.com/users/bkyryliuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bkyryliuk/subscriptions","organizations_url":"https://api.github.com/users/bkyryliuk/orgs","repos_url":"https://api.github.com/users/bkyryliuk/repos","events_url":"https://api.github.com/users/bkyryliuk/events{/privacy}","received_events_url":"https://api.github.com/users/bkyryliuk/received_events","type":"User","site_admin":false},"body":"Current change adds ability to execute the SQL queries on the celery worker.\r\nMore info about celery can be found here:\r\n\r\n- http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html \r\n\r\nWorker can be started via command `caravel worker`\r\nCelery is configured in the config.py and by default it is turned off. (`CELERY_CONFIG = None`)\r\nFixed some lint issues in the tests.\r\n\r\nTested:\r\n1. Manually tested if the query workes fine with the `CELERY_CONFIG = None` and with \r\n```\r\nclass CeleryConfig(object):\r\n    BROKER_URL = 'sqla+sqlite:///celerydb.sqlite'\r\n    CELERY_IMPORTS = ('caravel.tasks', )\r\n    CELERY_RESULT_BACKEND = 'db+sqlite:///celery_results.sqlite'\r\n    CELERY_ANNOTATIONS = {'tasks.add': {'rate_limit': '10/s'}}\r\nCELERY_CONFIG = CeleryConfig\r\n```\r\n\r\nTask is passed and executed:\r\n![screen shot 2016-07-13 at 1 21 17 pm](https://cloud.githubusercontent.com/assets/5727938/16818595/71dd3764-48fd-11e6-81c6-de9b77da9e87.png)\r\n\r\nQuery works for both cases:\r\n<img width=\"1410\" alt=\"screen shot 2016-07-13 at 1 21 31 pm\" src=\"https://cloud.githubusercontent.com/assets/5727938/16818594/6c22ab4c-48fd-11e6-99fa-db5cabf4027e.png\">\r\n\r\nReviewers:\r\n* @georgeke \r\n* @mistercrunch \r\n* @ascott ","created_at":"2016-07-13T21:04:19Z","updated_at":"2016-08-08T23:51:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3d9d19e9ffc3cbfe5aa86d38398b057968ea0512","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/airbnb/caravel/pulls/759/commits","review_comments_url":"https://api.github.com/repos/airbnb/caravel/pulls/759/comments","review_comment_url":"https://api.github.com/repos/airbnb/caravel/pulls/comments{/number}","comments_url":"https://api.github.com/repos/airbnb/caravel/issues/759/comments","statuses_url":"https://api.github.com/repos/airbnb/caravel/statuses/7ff31bb61212da8ead0117d3df4c0b0aef9f2233","head":{"label":"bkyryliuk:celery","ref":"celery","sha":"7ff31bb61212da8ead0117d3df4c0b0aef9f2233","user":{"login":"bkyryliuk","id":5727938,"avatar_url":"https://avatars.githubusercontent.com/u/5727938?v=3","gravatar_id":"","url":"https://api.github.com/users/bkyryliuk","html_url":"https://github.com/bkyryliuk","followers_url":"https://api.github.com/users/bkyryliuk/followers","following_url":"https://api.github.com/users/bkyryliuk/following{/other_user}","gists_url":"https://api.github.com/users/bkyryliuk/gists{/gist_id}","starred_url":"https://api.github.com/users/bkyryliuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bkyryliuk/subscriptions","organizations_url":"https://api.github.com/users/bkyryliuk/orgs","repos_url":"https://api.github.com/users/bkyryliuk/repos","events_url":"https://api.github.com/users/bkyryliuk/events{/privacy}","received_events_url":"https://api.github.com/users/bkyryliuk/received_events","type":"User","site_admin":false},"repo":{"id":61239887,"name":"caravel","full_name":"bkyryliuk/caravel","owner":{"login":"bkyryliuk","id":5727938,"avatar_url":"https://avatars.githubusercontent.com/u/5727938?v=3","gravatar_id":"","url":"https://api.github.com/users/bkyryliuk","html_url":"https://github.com/bkyryliuk","followers_url":"https://api.github.com/users/bkyryliuk/followers","following_url":"https://api.github.com/users/bkyryliuk/following{/other_user}","gists_url":"https://api.github.com/users/bkyryliuk/gists{/gist_id}","starred_url":"https://api.github.com/users/bkyryliuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bkyryliuk/subscriptions","organizations_url":"https://api.github.com/users/bkyryliuk/orgs","repos_url":"https://api.github.com/users/bkyryliuk/repos","events_url":"https://api.github.com/users/bkyryliuk/events{/privacy}","received_events_url":"https://api.github.com/users/bkyryliuk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bkyryliuk/caravel","description":"Caravel is a data exploration platform designed to be visual, intuitive, and interactive","fork":true,"url":"https://api.github.com/repos/bkyryliuk/caravel","forks_url":"https://api.github.com/repos/bkyryliuk/caravel/forks","keys_url":"https://api.github.com/repos/bkyryliuk/caravel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bkyryliuk/caravel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bkyryliuk/caravel/teams","hooks_url":"https://api.github.com/repos/bkyryliuk/caravel/hooks","issue_events_url":"https://api.github.com/repos/bkyryliuk/caravel/issues/events{/number}","events_url":"https://api.github.com/repos/bkyryliuk/caravel/events","assignees_url":"https://api.github.com/repos/bkyryliuk/caravel/assignees{/user}","branches_url":"https://api.github.com/repos/bkyryliuk/caravel/branches{/branch}","tags_url":"https://api.github.com/repos/bkyryliuk/caravel/tags","blobs_url":"https://api.github.com/repos/bkyryliuk/caravel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bkyryliuk/caravel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bkyryliuk/caravel/git/refs{/sha}","trees_url":"https://api.github.com/repos/bkyryliuk/caravel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bkyryliuk/caravel/statuses/{sha}","languages_url":"https://api.github.com/repos/bkyryliuk/caravel/languages","stargazers_url":"https://api.github.com/repos/bkyryliuk/caravel/stargazers","contributors_url":"https://api.github.com/repos/bkyryliuk/caravel/contributors","subscribers_url":"https://api.github.com/repos/bkyryliuk/caravel/subscribers","subscription_url":"https://api.github.com/repos/bkyryliuk/caravel/subscription","commits_url":"https://api.github.com/repos/bkyryliuk/caravel/commits{/sha}","git_commits_url":"https://api.github.com/repos/bkyryliuk/caravel/git/commits{/sha}","comments_url":"https://api.github.com/repos/bkyryliuk/caravel/comments{/number}","issue_comment_url":"https://api.github.com/repos/bkyryliuk/caravel/issues/comments{/number}","contents_url":"https://api.github.com/repos/bkyryliuk/caravel/contents/{+path}","compare_url":"https://api.github.com/repos/bkyryliuk/caravel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bkyryliuk/caravel/merges","archive_url":"https://api.github.com/repos/bkyryliuk/caravel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bkyryliuk/caravel/downloads","issues_url":"https://api.github.com/repos/bkyryliuk/caravel/issues{/number}","pulls_url":"https://api.github.com/repos/bkyryliuk/caravel/pulls{/number}","milestones_url":"https://api.github.com/repos/bkyryliuk/caravel/milestones{/number}","notifications_url":"https://api.github.com/repos/bkyryliuk/caravel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bkyryliuk/caravel/labels{/name}","releases_url":"https://api.github.com/repos/bkyryliuk/caravel/releases{/id}","deployments_url":"https://api.github.com/repos/bkyryliuk/caravel/deployments","created_at":"2016-06-15T20:47:09Z","updated_at":"2016-06-15T20:47:11Z","pushed_at":"2016-08-08T23:47:19Z","git_url":"git://github.com/bkyryliuk/caravel.git","ssh_url":"git@github.com:bkyryliuk/caravel.git","clone_url":"https://github.com/bkyryliuk/caravel.git","svn_url":"https://github.com/bkyryliuk/caravel","homepage":"","size":36792,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"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":"airbnb:master","ref":"master","sha":"b0a1f07818cc1454bb823933a783035f753d72e8","user":{"login":"airbnb","id":698437,"avatar_url":"https://avatars.githubusercontent.com/u/698437?v=3","gravatar_id":"","url":"https://api.github.com/users/airbnb","html_url":"https://github.com/airbnb","followers_url":"https://api.github.com/users/airbnb/followers","following_url":"https://api.github.com/users/airbnb/following{/other_user}","gists_url":"https://api.github.com/users/airbnb/gists{/gist_id}","starred_url":"https://api.github.com/users/airbnb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airbnb/subscriptions","organizations_url":"https://api.github.com/users/airbnb/orgs","repos_url":"https://api.github.com/users/airbnb/repos","events_url":"https://api.github.com/users/airbnb/events{/privacy}","received_events_url":"https://api.github.com/users/airbnb/received_events","type":"Organization","site_admin":false},"repo":{"id":39464018,"name":"caravel","full_name":"airbnb/caravel","owner":{"login":"airbnb","id":698437,"avatar_url":"https://avatars.githubusercontent.com/u/698437?v=3","gravatar_id":"","url":"https://api.github.com/users/airbnb","html_url":"https://github.com/airbnb","followers_url":"https://api.github.com/users/airbnb/followers","following_url":"https://api.github.com/users/airbnb/following{/other_user}","gists_url":"https://api.github.com/users/airbnb/gists{/gist_id}","starred_url":"https://api.github.com/users/airbnb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airbnb/subscriptions","organizations_url":"https://api.github.com/users/airbnb/orgs","repos_url":"https://api.github.com/users/airbnb/repos","events_url":"https://api.github.com/users/airbnb/events{/privacy}","received_events_url":"https://api.github.com/users/airbnb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/airbnb/caravel","description":"Caravel is a data exploration platform designed to be visual, intuitive, and interactive","fork":false,"url":"https://api.github.com/repos/airbnb/caravel","forks_url":"https://api.github.com/repos/airbnb/caravel/forks","keys_url":"https://api.github.com/repos/airbnb/caravel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/airbnb/caravel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/airbnb/caravel/teams","hooks_url":"https://api.github.com/repos/airbnb/caravel/hooks","issue_events_url":"https://api.github.com/repos/airbnb/caravel/issues/events{/number}","events_url":"https://api.github.com/repos/airbnb/caravel/events","assignees_url":"https://api.github.com/repos/airbnb/caravel/assignees{/user}","branches_url":"https://api.github.com/repos/airbnb/caravel/branches{/branch}","tags_url":"https://api.github.com/repos/airbnb/caravel/tags","blobs_url":"https://api.github.com/repos/airbnb/caravel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/airbnb/caravel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/airbnb/caravel/git/refs{/sha}","trees_url":"https://api.github.com/repos/airbnb/caravel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/airbnb/caravel/statuses/{sha}","languages_url":"https://api.github.com/repos/airbnb/caravel/languages","stargazers_url":"https://api.github.com/repos/airbnb/caravel/stargazers","contributors_url":"https://api.github.com/repos/airbnb/caravel/contributors","subscribers_url":"https://api.github.com/repos/airbnb/caravel/subscribers","subscription_url":"https://api.github.com/repos/airbnb/caravel/subscription","commits_url":"https://api.github.com/repos/airbnb/caravel/commits{/sha}","git_commits_url":"https://api.github.com/repos/airbnb/caravel/git/commits{/sha}","comments_url":"https://api.github.com/repos/airbnb/caravel/comments{/number}","issue_comment_url":"https://api.github.com/repos/airbnb/caravel/issues/comments{/number}","contents_url":"https://api.github.com/repos/airbnb/caravel/contents/{+path}","compare_url":"https://api.github.com/repos/airbnb/caravel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/airbnb/caravel/merges","archive_url":"https://api.github.com/repos/airbnb/caravel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/airbnb/caravel/downloads","issues_url":"https://api.github.com/repos/airbnb/caravel/issues{/number}","pulls_url":"https://api.github.com/repos/airbnb/caravel/pulls{/number}","milestones_url":"https://api.github.com/repos/airbnb/caravel/milestones{/number}","notifications_url":"https://api.github.com/repos/airbnb/caravel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/airbnb/caravel/labels{/name}","releases_url":"https://api.github.com/repos/airbnb/caravel/releases{/id}","deployments_url":"https://api.github.com/repos/airbnb/caravel/deployments","created_at":"2015-07-21T18:55:34Z","updated_at":"2016-08-08T19:11:43Z","pushed_at":"2016-08-08T23:47:19Z","git_url":"git://github.com/airbnb/caravel.git","ssh_url":"git@github.com:airbnb/caravel.git","clone_url":"https://github.com/airbnb/caravel.git","svn_url":"https://github.com/airbnb/caravel","homepage":"","size":39251,"stargazers_count":6885,"watchers_count":6885,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":733,"mirror_url":null,"open_issues_count":209,"forks":733,"open_issues":209,"watchers":6885,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/airbnb/caravel/pulls/759"},"html":{"href":"https://github.com/airbnb/caravel/pull/759"},"issue":{"href":"https://api.github.com/repos/airbnb/caravel/issues/759"},"comments":{"href":"https://api.github.com/repos/airbnb/caravel/issues/759/comments"},"review_comments":{"href":"https://api.github.com/repos/airbnb/caravel/pulls/759/comments"},"review_comment":{"href":"https://api.github.com/repos/airbnb/caravel/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/airbnb/caravel/pulls/759/commits"},"statuses":{"href":"https://api.github.com/repos/airbnb/caravel/statuses/7ff31bb61212da8ead0117d3df4c0b0aef9f2233"}}}}
{"actor":{"display_login":"mistercrunch"}}