{"action":"created","comment":{"url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/comments/234467385","pull_request_review_id":176111875,"id":234467385,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzNDQ2NzM4NQ==","diff_hunk":"@@ -0,0 +1,316 @@\n+import tests.context\n+import pytest\n+import time\n+from multiprocessing import Process\n+import os\n+import shutil\n+\n+from core.communication_manager         import CommunicationManager\n+from core.runner                        import DMLRunner\n+from core.scheduler                     import DMLScheduler\n+from core.configuration                 import ConfigurationManager\n+from tests.testing_utils                import make_initialize_job, make_model_json\n+from tests.testing_utils                import make_serialized_job, serialize_job\n+from core.utils.enums                   import RawEventTypes, JobTypes, MessageEventTypes\n+from core.utils.keras                   import serialize_weights\n+from core.blockchain.blockchain_gateway import BlockchainGateway\n+from core.blockchain.blockchain_utils   import setter, TxEnum\n+\n+\n+config_manager = ConfigurationManager()\n+config_manager.bootstrap(\n+    config_filepath='tests/artifacts/integration/configuration.ini'\n+)\n+\n+@pytest.fixture\n+def mnist_filepath():\n+    return 'tests/artifacts/integration/mnist'\n+\n+@pytest.fixture(scope='session')\n+def transformed_filepath():\n+    return 'tests/artifacts/integration/mnist'+'/transformed'\n+\n+@pytest.fixture(scope='session', autouse=True)\n+def cleanup(transformed_filepath):\n+    # Will be executed before the first test\n+    yield\n+    # Will be executed after the last test\n+    if os.path.isdir(transformed_filepath):\n+        shutil.rmtree(transformed_filepath)\n+\n+@pytest.fixture\n+def new_session_event(mnist_filepath):\n+    serialized_job = make_serialized_job(mnist_filepath)\n+    new_session_event = {\n+        TxEnum.KEY.name: None,\n+        TxEnum.CONTENT.name: {\n+            \"optimizer_params\": {\"num_averages_per_round\": 2, \"max_rounds\": 2},\n+            \"serialized_job\": serialized_job\n+        }\n+    }\n+    return new_session_event\n+\n+def setup_client(config_manager):\n+    \"\"\"\n+    Set up and return communication_manager, blockchain_gateway, scheduler\n+    \"\"\"\n+    communication_manager = CommunicationManager()\n+    blockchain_gateway = BlockchainGateway()\n+    scheduler = DMLScheduler(config_manager)\n+    communication_manager.configure(scheduler)\n+    blockchain_gateway.configure(config_manager, communication_manager)\n+    scheduler.configure(communication_manager)\n+    return communication_manager, blockchain_gateway, scheduler\n+\n+def test_federated_learning_two_clients_automated(new_session_event):\n+    \"\"\"\n+    Tests fully automated federated learning.\n+    \"\"\"\n+    # Set up first client\n+    communication_manager, blockchain_gateway, scheduler = setup_client(config_manager)\n+    # Set up second client\n+    communication_manager_2, blockchain_gateway_2, scheduler_2 = setup_client(config_manager)\n+    # (0) Someone sends decentralized learning event to the chain\n+    tx_receipt = setter(\n+        client=blockchain_gateway.client, \n+        key=None, \n+        port=blockchain_gateway.port, \n+        value=new_session_event, \n+        flag=True\n+    )\n+    assert tx_receipt\n+    scheduler.start_cron(period_in_mins=0.01)\n+    scheduler_2.start_cron(period_in_mins=0.01)\n+    blockchain_gateway.start_cron(period_in_mins=0.01)\n+    blockchain_gateway_2.start_cron(period_in_mins=0.01)\n+    timeout = 40 + time.time()\n+    while time.time() < timeout and (len(scheduler.processed) != 10 or len(scheduler_2.processed) != 10):\n+        time.sleep(5)\n+    scheduler.stop_cron()\n+    scheduler_2.stop_cron()\n+    blockchain_gateway.stop_cron()\n+    blockchain_gateway_2.stop_cron()\n+    assert len(scheduler.processed) == 10, \\\n+        \"Jobs {} failed/not completed in time!\".format([\n+        result.job.job_type for result in scheduler.processed])  \n+    assert len(scheduler_2.processed) == 10, \\\n+        \"Jobs {} failed/not completed in time!\".format([\n+        result.job.job_type for result in scheduler_2.processed])    \n+    assert communication_manager.optimizer is None\n+    assert communication_manager_2.optimizer is None\n+\n+def test_federated_learning_two_clients_manual(new_session_event):\n+    \"\"\"\n+    Integration test that checks that one round of federated learning can be\n+    COMPLETED with max_rounds = 2, num_averages_per_round = 2\n+\n+    This is everything that happens in this test:\n+\n+    \"\"\"\n+    # Set up first client\n+    communication_manager, blockchain_gateway, scheduler = setup_client(config_manager)\n+    # Set up second client\n+    communication_manager_2, blockchain_gateway_2, scheduler_2 = setup_client(config_manager)\n+    # set up new session\n+    # (0) Someone sends decentralized learning event to the chain\n+    tx_receipt = setter(blockchain_gateway.client, None, blockchain_gateway.port, new_session_event, True)\n+    assert tx_receipt\n+    # (1) Gateway_1 listens for the event\n+    blockchain_gateway._listen(blockchain_gateway._handle_new_session_creation, \n+        blockchain_gateway._filter_new_session)\n+    # (2) Gateway_2 listens for the event\n+    blockchain_gateway_2._listen(blockchain_gateway_2._handle_new_session_creation, \n+        blockchain_gateway_2._filter_new_session)\n+    # (3) Scheduler_1 and Scheduler_2 runs the following jobs:\n+        # (3a.1) JOB_INIT\n+        # (3a.2) JOB_SPLIT\n+        # (3b) JOB_TRAIN\n+        # (3c) JOB_COMM\n+    scheduler.start_cron(period_in_mins = 0.01)\n+    scheduler_2.start_cron(period_in_mins=0.01)\n+    timeout = time.time() + 25\n+    while time.time() < timeout and (len(scheduler.processed) != 4\\\n+        or len(scheduler_2.processed) != 4):\n+        time.sleep(5)\n+    assert len(scheduler.processed) == 4, \"Jobs failed/not completed in time!\"\n+    assert len(scheduler_2.processed) == 4, \"Jobs failed/not completed in time!\"\n+    # (4) Gateway_1 listens for the new weights \n+    blockchain_gateway._listen(blockchain_gateway._handle_new_session_info,\n+        blockchain_gateway._filter_new_session_info)\n+    # (6) Gateway_2 listens for the new weights\n+    blockchain_gateway_2._listen(blockchain_gateway_2._handle_new_session_info,\n+        blockchain_gateway_2._filter_new_session_info)\n+    # (8) Scheduler_1 and Scheduler_2 runs the following jobs:\n+        # (6a) JOB_AVG\n+        # (6b) JOB_AVG\n+        # (6a) JOB_TRAIN\n+        # (6a) JOB_COMM\n+    timeout = time.time() + 20\n+    while time.time() < timeout and (len(scheduler.processed) != 8\\\n+        or len(scheduler_2.processed) != 8):\n+        time.sleep(4)\n+    assert len(scheduler.processed) == 8, \\\n+        \"Jobs {} failed/not completed in time!\".format([\n+            result.job.job_type for result in scheduler.processed])\n+    assert len(scheduler_2.processed) == 8, \"Jobs failed/not completed in time!\"\n+    # (4) Gateway_1 listens for the new weights \n+    blockchain_gateway._listen(blockchain_gateway._handle_new_session_info,\n+        blockchain_gateway._filter_new_session_info)\n+    # (6) Gateway_2 listens for the new weights\n+    blockchain_gateway_2._listen(blockchain_gateway_2._handle_new_session_info,\n+        blockchain_gateway_2._filter_new_session_info)\n+    # (13) Optimizer tells Communication Manager to schedule JOB_AVG\n+    # (14) Scheduler_1 and Scheduler_2 runs the following jobs:\n+        # (9a) JOB_AVG\n+        # (9a) JOB_AVG\n+    timeout = time.time() + 10\n+    while time.time() < timeout and (len(scheduler.processed) != 10\\\n+        or len(scheduler_2.processed) != 10):\n+        time.sleep(2)","path":"tests/test_all_integration.py","position":169,"original_position":169,"commit_id":"e0e27d35899a3789c1b454797a4d319791e08827","original_commit_id":"e0e27d35899a3789c1b454797a4d319791e08827","user":{"login":"kiddyboots216","id":22651617,"node_id":"MDQ6VXNlcjIyNjUxNjE3","avatar_url":"https://avatars3.githubusercontent.com/u/22651617?v=4","gravatar_id":"","url":"https://api.github.com/users/kiddyboots216","html_url":"https://github.com/kiddyboots216","followers_url":"https://api.github.com/users/kiddyboots216/followers","following_url":"https://api.github.com/users/kiddyboots216/following{/other_user}","gists_url":"https://api.github.com/users/kiddyboots216/gists{/gist_id}","starred_url":"https://api.github.com/users/kiddyboots216/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kiddyboots216/subscriptions","organizations_url":"https://api.github.com/users/kiddyboots216/orgs","repos_url":"https://api.github.com/users/kiddyboots216/repos","events_url":"https://api.github.com/users/kiddyboots216/events{/privacy}","received_events_url":"https://api.github.com/users/kiddyboots216/received_events","type":"User","site_admin":false},"body":"Same as above","created_at":"2018-11-18T23:16:12Z","updated_at":"2018-11-18T23:16:12Z","html_url":"https://github.com/georgymh/decentralized-ml/pull/31#discussion_r234467385","pull_request_url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/comments/234467385"},"html":{"href":"https://github.com/georgymh/decentralized-ml/pull/31#discussion_r234467385"},"pull_request":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31"}},"in_reply_to_id":234467309},"pull_request":{"url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31","id":229859073,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI5ODU5MDcz","html_url":"https://github.com/georgymh/decentralized-ml/pull/31","diff_url":"https://github.com/georgymh/decentralized-ml/pull/31.diff","patch_url":"https://github.com/georgymh/decentralized-ml/pull/31.patch","issue_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues/31","number":31,"state":"open","locked":false,"title":"Communication job","user":{"login":"kiddyboots216","id":22651617,"node_id":"MDQ6VXNlcjIyNjUxNjE3","avatar_url":"https://avatars3.githubusercontent.com/u/22651617?v=4","gravatar_id":"","url":"https://api.github.com/users/kiddyboots216","html_url":"https://github.com/kiddyboots216","followers_url":"https://api.github.com/users/kiddyboots216/followers","following_url":"https://api.github.com/users/kiddyboots216/following{/other_user}","gists_url":"https://api.github.com/users/kiddyboots216/gists{/gist_id}","starred_url":"https://api.github.com/users/kiddyboots216/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kiddyboots216/subscriptions","organizations_url":"https://api.github.com/users/kiddyboots216/orgs","repos_url":"https://api.github.com/users/kiddyboots216/repos","events_url":"https://api.github.com/users/kiddyboots216/events{/privacy}","received_events_url":"https://api.github.com/users/kiddyboots216/received_events","type":"User","site_admin":false},"body":"This PR currently\r\n- Adds a Communication Job to the Runner by utilizing the setter in blockchain_utils\r\n- Adds support for JOB_COMM to the Optimizer, where previously JOB_COMM was just a dummy job\r\n- Reimplements JOB_AVG because nobody would let me put JOB_COMM with JOB_AVG so the ENTIRE AVERAGING PR was wrong. LOL.\r\n- Integrates communication with support for JOB_AVG to the Optimizer by communicating an entire job rather than just weights (sparse amount of metadata is added and the tradeoff in usability/extendability is well worth it)\r\n- Uses TxEnum everywhere to support integration with blockchain rather than hardcoded values\r\n- Adds tests for above functionality to fed_avg_optimizer, runner, and scheduler\r\n- Adds Blockchain Gateway to Bootstrapper in the same way that Scheduler is used in Bootstrapper\r\n- Adds an integration testing file which comprehensively tests federated learning between 2 clients (automated by mimicking the bootstrapper), fed. learning between 2 clients (manual), and the communication manager (more in-depth and shows what's going on behind the scenes)","created_at":"2018-11-10T01:09:31Z","updated_at":"2018-11-18T23:16:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"81d0082c6d6312434279ca152dff21788331d0c8","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31/commits","review_comments_url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31/comments","review_comment_url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/comments{/number}","comments_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues/31/comments","statuses_url":"https://api.github.com/repos/georgymh/decentralized-ml/statuses/e0e27d35899a3789c1b454797a4d319791e08827","head":{"label":"georgymh:communication-job","ref":"communication-job","sha":"e0e27d35899a3789c1b454797a4d319791e08827","user":{"login":"georgymh","id":7425056,"node_id":"MDQ6VXNlcjc0MjUwNTY=","avatar_url":"https://avatars3.githubusercontent.com/u/7425056?v=4","gravatar_id":"","url":"https://api.github.com/users/georgymh","html_url":"https://github.com/georgymh","followers_url":"https://api.github.com/users/georgymh/followers","following_url":"https://api.github.com/users/georgymh/following{/other_user}","gists_url":"https://api.github.com/users/georgymh/gists{/gist_id}","starred_url":"https://api.github.com/users/georgymh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/georgymh/subscriptions","organizations_url":"https://api.github.com/users/georgymh/orgs","repos_url":"https://api.github.com/users/georgymh/repos","events_url":"https://api.github.com/users/georgymh/events{/privacy}","received_events_url":"https://api.github.com/users/georgymh/received_events","type":"User","site_admin":false},"repo":{"id":133437202,"node_id":"MDEwOlJlcG9zaXRvcnkxMzM0MzcyMDI=","name":"decentralized-ml","full_name":"georgymh/decentralized-ml","private":false,"owner":{"login":"georgymh","id":7425056,"node_id":"MDQ6VXNlcjc0MjUwNTY=","avatar_url":"https://avatars3.githubusercontent.com/u/7425056?v=4","gravatar_id":"","url":"https://api.github.com/users/georgymh","html_url":"https://github.com/georgymh","followers_url":"https://api.github.com/users/georgymh/followers","following_url":"https://api.github.com/users/georgymh/following{/other_user}","gists_url":"https://api.github.com/users/georgymh/gists{/gist_id}","starred_url":"https://api.github.com/users/georgymh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/georgymh/subscriptions","organizations_url":"https://api.github.com/users/georgymh/orgs","repos_url":"https://api.github.com/users/georgymh/repos","events_url":"https://api.github.com/users/georgymh/events{/privacy}","received_events_url":"https://api.github.com/users/georgymh/received_events","type":"User","site_admin":false},"html_url":"https://github.com/georgymh/decentralized-ml","description":"Interoperable and decentralized machine learning.","fork":false,"url":"https://api.github.com/repos/georgymh/decentralized-ml","forks_url":"https://api.github.com/repos/georgymh/decentralized-ml/forks","keys_url":"https://api.github.com/repos/georgymh/decentralized-ml/keys{/key_id}","collaborators_url":"https://api.github.com/repos/georgymh/decentralized-ml/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/georgymh/decentralized-ml/teams","hooks_url":"https://api.github.com/repos/georgymh/decentralized-ml/hooks","issue_events_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues/events{/number}","events_url":"https://api.github.com/repos/georgymh/decentralized-ml/events","assignees_url":"https://api.github.com/repos/georgymh/decentralized-ml/assignees{/user}","branches_url":"https://api.github.com/repos/georgymh/decentralized-ml/branches{/branch}","tags_url":"https://api.github.com/repos/georgymh/decentralized-ml/tags","blobs_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/refs{/sha}","trees_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/trees{/sha}","statuses_url":"https://api.github.com/repos/georgymh/decentralized-ml/statuses/{sha}","languages_url":"https://api.github.com/repos/georgymh/decentralized-ml/languages","stargazers_url":"https://api.github.com/repos/georgymh/decentralized-ml/stargazers","contributors_url":"https://api.github.com/repos/georgymh/decentralized-ml/contributors","subscribers_url":"https://api.github.com/repos/georgymh/decentralized-ml/subscribers","subscription_url":"https://api.github.com/repos/georgymh/decentralized-ml/subscription","commits_url":"https://api.github.com/repos/georgymh/decentralized-ml/commits{/sha}","git_commits_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/commits{/sha}","comments_url":"https://api.github.com/repos/georgymh/decentralized-ml/comments{/number}","issue_comment_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues/comments{/number}","contents_url":"https://api.github.com/repos/georgymh/decentralized-ml/contents/{+path}","compare_url":"https://api.github.com/repos/georgymh/decentralized-ml/compare/{base}...{head}","merges_url":"https://api.github.com/repos/georgymh/decentralized-ml/merges","archive_url":"https://api.github.com/repos/georgymh/decentralized-ml/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/georgymh/decentralized-ml/downloads","issues_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues{/number}","pulls_url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls{/number}","milestones_url":"https://api.github.com/repos/georgymh/decentralized-ml/milestones{/number}","notifications_url":"https://api.github.com/repos/georgymh/decentralized-ml/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/georgymh/decentralized-ml/labels{/name}","releases_url":"https://api.github.com/repos/georgymh/decentralized-ml/releases{/id}","deployments_url":"https://api.github.com/repos/georgymh/decentralized-ml/deployments","created_at":"2018-05-15T00:31:38Z","updated_at":"2018-11-18T03:06:41Z","pushed_at":"2018-11-18T03:06:58Z","git_url":"git://github.com/georgymh/decentralized-ml.git","ssh_url":"git@github.com:georgymh/decentralized-ml.git","clone_url":"https://github.com/georgymh/decentralized-ml.git","svn_url":"https://github.com/georgymh/decentralized-ml","homepage":"","size":9529,"stargazers_count":3,"watchers_count":3,"language":"Jupyter Notebook","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":3,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":1,"open_issues":3,"watchers":3,"default_branch":"master"}},"base":{"label":"georgymh:master","ref":"master","sha":"01c9bf739cceaf2a5f26a2ca44f9b8f548fa59b3","user":{"login":"georgymh","id":7425056,"node_id":"MDQ6VXNlcjc0MjUwNTY=","avatar_url":"https://avatars3.githubusercontent.com/u/7425056?v=4","gravatar_id":"","url":"https://api.github.com/users/georgymh","html_url":"https://github.com/georgymh","followers_url":"https://api.github.com/users/georgymh/followers","following_url":"https://api.github.com/users/georgymh/following{/other_user}","gists_url":"https://api.github.com/users/georgymh/gists{/gist_id}","starred_url":"https://api.github.com/users/georgymh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/georgymh/subscriptions","organizations_url":"https://api.github.com/users/georgymh/orgs","repos_url":"https://api.github.com/users/georgymh/repos","events_url":"https://api.github.com/users/georgymh/events{/privacy}","received_events_url":"https://api.github.com/users/georgymh/received_events","type":"User","site_admin":false},"repo":{"id":133437202,"node_id":"MDEwOlJlcG9zaXRvcnkxMzM0MzcyMDI=","name":"decentralized-ml","full_name":"georgymh/decentralized-ml","private":false,"owner":{"login":"georgymh","id":7425056,"node_id":"MDQ6VXNlcjc0MjUwNTY=","avatar_url":"https://avatars3.githubusercontent.com/u/7425056?v=4","gravatar_id":"","url":"https://api.github.com/users/georgymh","html_url":"https://github.com/georgymh","followers_url":"https://api.github.com/users/georgymh/followers","following_url":"https://api.github.com/users/georgymh/following{/other_user}","gists_url":"https://api.github.com/users/georgymh/gists{/gist_id}","starred_url":"https://api.github.com/users/georgymh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/georgymh/subscriptions","organizations_url":"https://api.github.com/users/georgymh/orgs","repos_url":"https://api.github.com/users/georgymh/repos","events_url":"https://api.github.com/users/georgymh/events{/privacy}","received_events_url":"https://api.github.com/users/georgymh/received_events","type":"User","site_admin":false},"html_url":"https://github.com/georgymh/decentralized-ml","description":"Interoperable and decentralized machine learning.","fork":false,"url":"https://api.github.com/repos/georgymh/decentralized-ml","forks_url":"https://api.github.com/repos/georgymh/decentralized-ml/forks","keys_url":"https://api.github.com/repos/georgymh/decentralized-ml/keys{/key_id}","collaborators_url":"https://api.github.com/repos/georgymh/decentralized-ml/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/georgymh/decentralized-ml/teams","hooks_url":"https://api.github.com/repos/georgymh/decentralized-ml/hooks","issue_events_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues/events{/number}","events_url":"https://api.github.com/repos/georgymh/decentralized-ml/events","assignees_url":"https://api.github.com/repos/georgymh/decentralized-ml/assignees{/user}","branches_url":"https://api.github.com/repos/georgymh/decentralized-ml/branches{/branch}","tags_url":"https://api.github.com/repos/georgymh/decentralized-ml/tags","blobs_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/refs{/sha}","trees_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/trees{/sha}","statuses_url":"https://api.github.com/repos/georgymh/decentralized-ml/statuses/{sha}","languages_url":"https://api.github.com/repos/georgymh/decentralized-ml/languages","stargazers_url":"https://api.github.com/repos/georgymh/decentralized-ml/stargazers","contributors_url":"https://api.github.com/repos/georgymh/decentralized-ml/contributors","subscribers_url":"https://api.github.com/repos/georgymh/decentralized-ml/subscribers","subscription_url":"https://api.github.com/repos/georgymh/decentralized-ml/subscription","commits_url":"https://api.github.com/repos/georgymh/decentralized-ml/commits{/sha}","git_commits_url":"https://api.github.com/repos/georgymh/decentralized-ml/git/commits{/sha}","comments_url":"https://api.github.com/repos/georgymh/decentralized-ml/comments{/number}","issue_comment_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues/comments{/number}","contents_url":"https://api.github.com/repos/georgymh/decentralized-ml/contents/{+path}","compare_url":"https://api.github.com/repos/georgymh/decentralized-ml/compare/{base}...{head}","merges_url":"https://api.github.com/repos/georgymh/decentralized-ml/merges","archive_url":"https://api.github.com/repos/georgymh/decentralized-ml/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/georgymh/decentralized-ml/downloads","issues_url":"https://api.github.com/repos/georgymh/decentralized-ml/issues{/number}","pulls_url":"https://api.github.com/repos/georgymh/decentralized-ml/pulls{/number}","milestones_url":"https://api.github.com/repos/georgymh/decentralized-ml/milestones{/number}","notifications_url":"https://api.github.com/repos/georgymh/decentralized-ml/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/georgymh/decentralized-ml/labels{/name}","releases_url":"https://api.github.com/repos/georgymh/decentralized-ml/releases{/id}","deployments_url":"https://api.github.com/repos/georgymh/decentralized-ml/deployments","created_at":"2018-05-15T00:31:38Z","updated_at":"2018-11-18T03:06:41Z","pushed_at":"2018-11-18T03:06:58Z","git_url":"git://github.com/georgymh/decentralized-ml.git","ssh_url":"git@github.com:georgymh/decentralized-ml.git","clone_url":"https://github.com/georgymh/decentralized-ml.git","svn_url":"https://github.com/georgymh/decentralized-ml","homepage":"","size":9529,"stargazers_count":3,"watchers_count":3,"language":"Jupyter Notebook","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":3,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":1,"open_issues":3,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31"},"html":{"href":"https://github.com/georgymh/decentralized-ml/pull/31"},"issue":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/issues/31"},"comments":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/issues/31/comments"},"review_comments":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31/comments"},"review_comment":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/pulls/31/commits"},"statuses":{"href":"https://api.github.com/repos/georgymh/decentralized-ml/statuses/e0e27d35899a3789c1b454797a4d319791e08827"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"kiddyboots216"}}