{"action":"created","comment":{"url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/comments/128268768","pull_request_review_id":50932386,"id":128268768,"diff_hunk":"@@ -0,0 +1,449 @@\n+from __future__ import print_function\n+from __future__ import division\n+from __future__ import absolute_import\n+\n+import logging\n+import webapp2\n+import os\n+import binascii\n+import json\n+import time\n+from threading import Timer\n+from collections import defaultdict\n+\n+import monkey_json\n+from priority_queue import PriorityQueue\n+\n+logging.getLogger().setLevel(logging.INFO)\n+\"\"\"\n+We are currently ignoring the project name in all our queries\n+\"\"\"\n+\n+class Queue(object):\n+    \"\"\"\n+    TODO: keep track of stats for\n+    https://cloud.google.com/appengine/docs/standard/python/taskqueue/rest/taskqueues#resource\n+    \"\"\"\n+    def __init__(self):\n+        self._tasks = {}\n+        self._pq = PriorityQueue()\n+\n+    @property\n+    def tasks(self):\n+        return self._tasks\n+\n+    @property\n+    def pq(self):\n+        return self._pq\n+\n+    def __len__(self):\n+        return len(self._tasks)\n+\n+queues = defaultdict(Queue)\n+def restart_module():\n+    global queues\n+    queues = defaultdict(Queue)\n+\n+def make_random_token():\n+    \"\"\"Return a 20-byte (40 character) random hex string.\"\"\"\n+    return binascii.hexlify(os.urandom(20))\n+\n+def now():\n+    \"\"\"\n+    microseconds since 00:00:00 (UTC), Thursday, 1 January 1970\n+    minus the number of leap seconds that have taken place since then.\n+    \"\"\"\n+    return int(time.time() * 10**6)\n+\n+class Task(object):\n+\n+    def __init__(self, id=None, payloadBase64=None,\n+        queueName=None, retry_count=5, tag='',\n+        dependencies=[]):\n+        \n+        assert queueName\n+        self._queueName = queueName\n+        self._queue = queues[self._queueName]\n+        if id is None:\n+            while True:\n+                id =  make_random_token()\n+                if id not in self._queue.tasks:\n+                    break\n+        elif id in self._queue.tasks:\n+            raise ValueError(id + \" already exists\")\n+        elif id == 'lease':\n+            #we wouldn't know if you want to update a task\n+            #or lease some tasks\n+            raise ValueError(id + \" is not a valid task name\")\n+\n+        self._id = id\n+\n+        assert payloadBase64\n+        self._payloadBase64 = payloadBase64\n+\n+\n+        self._retry_count = retry_count\n+\n+        self._tag = tag\n+\n+        self._parents = set()\n+        self._children = set(dependencies)\n+        for child in dependencies:\n+            # we might have already deleted one of the dependencies\n+            # when this task is submitted\n+            # so we will ignore dependencies which doesn't exists\n+            if child in self._queue.tasks:\n+                self._queue.tasks[child]._parents.add(self.id)\n+\n+        self._enqueueTimestamp = now()\n+        self._leaseTimeStamp = 0\n+\n+        self._in_pq = False\n+        self.maybe_insert_into_pq()\n+\n+    @property\n+    def enqueueTimestamp(self):\n+        \"\"\"\n+        long[Not mutable.] Time (in microseconds since the epoch) at which the task was enqueued.\n+        \"\"\"\n+        return self._enqueueTimestamp\n+\n+    @property\n+    def id(self):\n+        \"\"\"  \n+        string  Randomly generated name of the task. You may override the default generated name by specifying your own name when inserting the task.\n+        We strongly recommend against specifying a task name yourself.\n+        \"\"\"\n+        return self._id\n+    \n+    @property\n+    def kind(self):\n+        \"\"\"\n+        string  [Not mutable.] The kind of object returned, in this case set to task.\n+        \"\"\"\n+        return \"taskqueue#task\"\n+\n+    @property\n+    def leaseTimestamp(self):\n+        \"\"\"  \n+        The time at which the task lease will expire, in microseconds since the epoch. \n+        If this task has never been leased, it will be zero. \n+        If this this task has been previously leased and the lease has expired, this value will be < Now().\n+        \n+        On task.insert, this must be set to the time at which the task will first be available for lease. \n+        On task.lease, this property is not mutable and reflects the time at which the task lease will expire.  \n+        \"\"\"\n+        return self._leaseTimeStamp\n+\n+    @property\n+    def payloadBase64(self):\n+        \"\"\"\n+        string  [Not mutable.] The bytes describing the task to perform. \n+        This is a base64-encoded string.\n+        The client is expected to understand the payload format. \n+        The maximum size is 1MB. This value will be empty in calls to tasks.list.\n+        Required for calls to tasks.insert. \n+        \"\"\"\n+        return self._payloadBase64\n+\n+    @property\n+    def queueName(self):\n+        \"\"\"\n+        string  [Not mutable.] Name of the queue that the task is in.\n+        Required for calls to tasks.insert. \n+        \"\"\"\n+        return self._queueName\n+    \n+    @property\n+    def retry_count(self):\n+        \"\"\"\n+        integer The number of leases applied to this task.  \n+        \"\"\"\n+        return self._retry_count\n+\n+    @property\n+    def tag(self):\n+        \"\"\"\n+        string  The tag for this task. \n+        Tagging tasks allows you to group them for processing using lease.group_by_tag.\n+        \"\"\"\n+        return self._tag\n+\n+    @property\n+    def children(self):\n+        \"\"\"\n+        List of tasks' id which need to be fullfilled\n+        before this task can be leased\n+        \"\"\"\n+        return list(self._children)\n+\n+    def delete(self):\n+        for p in self._parents:\n+            # it cannot have been deleted\n+            # without deleting this task first\n+            assert p in self._queue.tasks\n+            self._queue.tasks[p]._children.remove(self.id)\n+\n+    def dependencies_met(self):\n+        return not self._children\n+\n+    def to_json(self):\n+        return json.dumps(self.to_dict())\n+\n+    def to_dict(self):\n+        return {\n+          \"kind\": self.kind,\n+          \"id\": self.id,\n+          \"queueName\": self.queueName,\n+          \"payloadBase64\": self.payloadBase64,\n+          \"enqueueTimestamp\": self.enqueueTimestamp,\n+          \"leaseTimestamp\": self.leaseTimestamp,\n+          \"retry_count\": self.retry_count,\n+          \"tag\": self.tag,\n+          \"children\": self.children\n+        }\n+\n+\n+    def maybe_insert_into_pq(self):\n+        \"\"\"\n+        We only want to have items in the priority queue\n+        which can be leased, so we check that dependencies\n+        are met before inserting.\n+\n+        We use the time of insertion as the priority.\n+        Which will be similar to enqueueTimestamp when\n+        first inseting a task for first time. (with met dependencies)\n+\n+        And similar to leaseTimeStamp when retrying a task.\n+        This means that when a task is retried it get pushed to the end\n+        of the queue. If we would insert with enqueueTimestamp after a \n+        retry it would get executed inmediately.\n+        \"\"\"\n+        if not self._children and not self._in_pq:\n+            self._in_pq = True\n+            self._queue.pq.add_item(now() , self.id)\n+\n+\n+    def _set_retry_timer(self, lease_for):\n+        \"\"\"\n+        Re insert in pq after certain time\n+        \"\"\"\n+        Timer(lease_for, self.maybe_insert_into_pq, ()).start()\n+ \n+    def lease(self, lease_for):\n+        \"\"\"\n+        time to lease for in seconds\n+        \"\"\"\n+        assert self._leaseTimeStamp <= now()\n+        self._leaseTimeStamp = now() + lease_for * 10**6\n+        self._set_retry_timer(lease_for)\n+\n+    def __hash__(self):\n+        return hash(self.id)\n+\n+\n+class TaskHandler(webapp2.RequestHandler):\n+\n+\n+    def get(self, project_name, taskqueue_name, maybe_task_name):\n+        queue =  queues[taskqueue_name]\n+        if not maybe_task_name:\n+            self._list_task(queue)\n+        else:\n+            self._get_task(queue, maybe_task_name)\n+\n+\n+    def post(self, project_name, taskqueue_name, maybe_lease_or_task_name):\n+        queue =  queues[taskqueue_name]\n+        if maybe_lease_or_task_name == 'lease':\n+            numTasks = int(self.request.get('numTasks', default_value='1'))\n+            assert numTasks >= 1\n+            leaseSecs = int(self.request.get('leaseSecs', default_value='1'))\n+            assert leaseSecs >= 1\n+            tag = self.request.get('tag', default_value='')\n+\n+            self._lease_task(queue, numTasks, leaseSecs, tag)\n+        elif maybe_lease_or_task_name == '':\n+            self._insert_task(queue, taskqueue_name)\n+        else:\n+            newLeaseSeconds = int(self.request.get('newLeaseSeconds', default_value='1'))\n+            assert newLeaseSeconds >= 1\n+            self._update_task(queue, maybe_lease_or_task_name, newLeaseSeconds)\n+\n+    def patch(self, project_name, taskqueue_name, task_name):\n+        queue =  queues[taskqueue_name]\n+        newLeaseSeconds = int(self.request.get('newLeaseSeconds', default_value='1'))\n+        assert newLeaseSeconds >= 1\n+        self._patch_task(queue, task_name, newLeaseSeconds)\n+\n+    def delete(self, project_name, taskqueue_name, task_name):\n+        queue =  queues[taskqueue_name]\n+        self._delete_task(queue, task_name)\n+\n+    def _delete_task(self, queue, task_name):\n+        \"\"\"\n+        DELETE  /projectname/taskqueue/queuename/tasks/taskname","path":"appengine/queue/taskqueue.py","position":285,"original_position":285,"commit_id":"27502fd9e6a46444e22f090a3a49785c860cc5c7","original_commit_id":"27502fd9e6a46444e22f090a3a49785c860cc5c7","user":{"login":"nkemnitz","id":8644150,"avatar_url":"https://avatars0.githubusercontent.com/u/8644150?v=4","gravatar_id":"","url":"https://api.github.com/users/nkemnitz","html_url":"https://github.com/nkemnitz","followers_url":"https://api.github.com/users/nkemnitz/followers","following_url":"https://api.github.com/users/nkemnitz/following{/other_user}","gists_url":"https://api.github.com/users/nkemnitz/gists{/gist_id}","starred_url":"https://api.github.com/users/nkemnitz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nkemnitz/subscriptions","organizations_url":"https://api.github.com/users/nkemnitz/orgs","repos_url":"https://api.github.com/users/nkemnitz/repos","events_url":"https://api.github.com/users/nkemnitz/events{/privacy}","received_events_url":"https://api.github.com/users/nkemnitz/received_events","type":"User","site_admin":false},"body":"Can you emphasize the parts of the call the user has to fill in? Maybe `/${projectname}/taskqueue/${queuename}/tasks/${taskname}` or something like that? Same for all the other requests.","created_at":"2017-07-19T14:47:49Z","updated_at":"2017-07-19T14:47:49Z","html_url":"https://github.com/seung-lab/neuroglancer/pull/153#discussion_r128268768","pull_request_url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153","_links":{"self":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/comments/128268768"},"html":{"href":"https://github.com/seung-lab/neuroglancer/pull/153#discussion_r128268768"},"pull_request":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153"}}},"pull_request":{"url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153","id":130473322,"html_url":"https://github.com/seung-lab/neuroglancer/pull/153","diff_url":"https://github.com/seung-lab/neuroglancer/pull/153.diff","patch_url":"https://github.com/seung-lab/neuroglancer/pull/153.patch","issue_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues/153","number":153,"state":"open","locked":false,"title":"feat(appengine/queue): Consistent task queue with dependencies.","user":{"login":"tartavull","id":4648166,"avatar_url":"https://avatars2.githubusercontent.com/u/4648166?v=4","gravatar_id":"","url":"https://api.github.com/users/tartavull","html_url":"https://github.com/tartavull","followers_url":"https://api.github.com/users/tartavull/followers","following_url":"https://api.github.com/users/tartavull/following{/other_user}","gists_url":"https://api.github.com/users/tartavull/gists{/gist_id}","starred_url":"https://api.github.com/users/tartavull/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tartavull/subscriptions","organizations_url":"https://api.github.com/users/tartavull/orgs","repos_url":"https://api.github.com/users/tartavull/repos","events_url":"https://api.github.com/users/tartavull/events{/privacy}","received_events_url":"https://api.github.com/users/tartavull/received_events","type":"User","site_admin":false},"body":"We need dependecies between tasks.\r\nWe have relatively low thoughput of tasks, because each tasks\r\nusually takes serval minutes.\r\n\r\nWidely used task queues doesn't support depedencies, so we\r\ndecided to reinvet the wheel and write yet another task queue.\r\nThis taskqueue, closely follows Google's Pull Queue API.","created_at":"2017-07-13T21:38:17Z","updated_at":"2017-07-19T14:47:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"56013f6b98354fe467d89a3250938124c7f417b1","assignee":null,"assignees":[],"requested_reviewers":[{"login":"wongwill86","id":1668987,"avatar_url":"https://avatars1.githubusercontent.com/u/1668987?v=4","gravatar_id":"","url":"https://api.github.com/users/wongwill86","html_url":"https://github.com/wongwill86","followers_url":"https://api.github.com/users/wongwill86/followers","following_url":"https://api.github.com/users/wongwill86/following{/other_user}","gists_url":"https://api.github.com/users/wongwill86/gists{/gist_id}","starred_url":"https://api.github.com/users/wongwill86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wongwill86/subscriptions","organizations_url":"https://api.github.com/users/wongwill86/orgs","repos_url":"https://api.github.com/users/wongwill86/repos","events_url":"https://api.github.com/users/wongwill86/events{/privacy}","received_events_url":"https://api.github.com/users/wongwill86/received_events","type":"User","site_admin":false},{"login":"william-silversmith","id":2517065,"avatar_url":"https://avatars1.githubusercontent.com/u/2517065?v=4","gravatar_id":"","url":"https://api.github.com/users/william-silversmith","html_url":"https://github.com/william-silversmith","followers_url":"https://api.github.com/users/william-silversmith/followers","following_url":"https://api.github.com/users/william-silversmith/following{/other_user}","gists_url":"https://api.github.com/users/william-silversmith/gists{/gist_id}","starred_url":"https://api.github.com/users/william-silversmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/william-silversmith/subscriptions","organizations_url":"https://api.github.com/users/william-silversmith/orgs","repos_url":"https://api.github.com/users/william-silversmith/repos","events_url":"https://api.github.com/users/william-silversmith/events{/privacy}","received_events_url":"https://api.github.com/users/william-silversmith/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153/commits","review_comments_url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153/comments","review_comment_url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/comments{/number}","comments_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues/153/comments","statuses_url":"https://api.github.com/repos/seung-lab/neuroglancer/statuses/27502fd9e6a46444e22f090a3a49785c860cc5c7","head":{"label":"seung-lab:it_queue","ref":"it_queue","sha":"27502fd9e6a46444e22f090a3a49785c860cc5c7","user":{"login":"seung-lab","id":796718,"avatar_url":"https://avatars0.githubusercontent.com/u/796718?v=4","gravatar_id":"","url":"https://api.github.com/users/seung-lab","html_url":"https://github.com/seung-lab","followers_url":"https://api.github.com/users/seung-lab/followers","following_url":"https://api.github.com/users/seung-lab/following{/other_user}","gists_url":"https://api.github.com/users/seung-lab/gists{/gist_id}","starred_url":"https://api.github.com/users/seung-lab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seung-lab/subscriptions","organizations_url":"https://api.github.com/users/seung-lab/orgs","repos_url":"https://api.github.com/users/seung-lab/repos","events_url":"https://api.github.com/users/seung-lab/events{/privacy}","received_events_url":"https://api.github.com/users/seung-lab/received_events","type":"Organization","site_admin":false},"repo":{"id":76393283,"name":"neuroglancer","full_name":"seung-lab/neuroglancer","owner":{"login":"seung-lab","id":796718,"avatar_url":"https://avatars0.githubusercontent.com/u/796718?v=4","gravatar_id":"","url":"https://api.github.com/users/seung-lab","html_url":"https://github.com/seung-lab","followers_url":"https://api.github.com/users/seung-lab/followers","following_url":"https://api.github.com/users/seung-lab/following{/other_user}","gists_url":"https://api.github.com/users/seung-lab/gists{/gist_id}","starred_url":"https://api.github.com/users/seung-lab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seung-lab/subscriptions","organizations_url":"https://api.github.com/users/seung-lab/orgs","repos_url":"https://api.github.com/users/seung-lab/repos","events_url":"https://api.github.com/users/seung-lab/events{/privacy}","received_events_url":"https://api.github.com/users/seung-lab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/seung-lab/neuroglancer","description":"WebGL-based viewer for volumetric data","fork":true,"url":"https://api.github.com/repos/seung-lab/neuroglancer","forks_url":"https://api.github.com/repos/seung-lab/neuroglancer/forks","keys_url":"https://api.github.com/repos/seung-lab/neuroglancer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/seung-lab/neuroglancer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/seung-lab/neuroglancer/teams","hooks_url":"https://api.github.com/repos/seung-lab/neuroglancer/hooks","issue_events_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues/events{/number}","events_url":"https://api.github.com/repos/seung-lab/neuroglancer/events","assignees_url":"https://api.github.com/repos/seung-lab/neuroglancer/assignees{/user}","branches_url":"https://api.github.com/repos/seung-lab/neuroglancer/branches{/branch}","tags_url":"https://api.github.com/repos/seung-lab/neuroglancer/tags","blobs_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/refs{/sha}","trees_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/seung-lab/neuroglancer/statuses/{sha}","languages_url":"https://api.github.com/repos/seung-lab/neuroglancer/languages","stargazers_url":"https://api.github.com/repos/seung-lab/neuroglancer/stargazers","contributors_url":"https://api.github.com/repos/seung-lab/neuroglancer/contributors","subscribers_url":"https://api.github.com/repos/seung-lab/neuroglancer/subscribers","subscription_url":"https://api.github.com/repos/seung-lab/neuroglancer/subscription","commits_url":"https://api.github.com/repos/seung-lab/neuroglancer/commits{/sha}","git_commits_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/commits{/sha}","comments_url":"https://api.github.com/repos/seung-lab/neuroglancer/comments{/number}","issue_comment_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues/comments{/number}","contents_url":"https://api.github.com/repos/seung-lab/neuroglancer/contents/{+path}","compare_url":"https://api.github.com/repos/seung-lab/neuroglancer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/seung-lab/neuroglancer/merges","archive_url":"https://api.github.com/repos/seung-lab/neuroglancer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/seung-lab/neuroglancer/downloads","issues_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues{/number}","pulls_url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls{/number}","milestones_url":"https://api.github.com/repos/seung-lab/neuroglancer/milestones{/number}","notifications_url":"https://api.github.com/repos/seung-lab/neuroglancer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/seung-lab/neuroglancer/labels{/name}","releases_url":"https://api.github.com/repos/seung-lab/neuroglancer/releases{/id}","deployments_url":"https://api.github.com/repos/seung-lab/neuroglancer/deployments","created_at":"2016-12-13T19:54:36Z","updated_at":"2017-07-07T14:51:43Z","pushed_at":"2017-07-18T20:34:11Z","git_url":"git://github.com/seung-lab/neuroglancer.git","ssh_url":"git@github.com:seung-lab/neuroglancer.git","clone_url":"https://github.com/seung-lab/neuroglancer.git","svn_url":"https://github.com/seung-lab/neuroglancer","homepage":"https://neuroglancer-demo.appspot.com","size":7141,"stargazers_count":3,"watchers_count":3,"language":"TypeScript","has_issues":true,"has_projects":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":78,"forks":1,"open_issues":78,"watchers":3,"default_branch":"master"}},"base":{"label":"seung-lab:master","ref":"master","sha":"cbfb01b29a54062683e07a5512ac6c5642aa5b77","user":{"login":"seung-lab","id":796718,"avatar_url":"https://avatars0.githubusercontent.com/u/796718?v=4","gravatar_id":"","url":"https://api.github.com/users/seung-lab","html_url":"https://github.com/seung-lab","followers_url":"https://api.github.com/users/seung-lab/followers","following_url":"https://api.github.com/users/seung-lab/following{/other_user}","gists_url":"https://api.github.com/users/seung-lab/gists{/gist_id}","starred_url":"https://api.github.com/users/seung-lab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seung-lab/subscriptions","organizations_url":"https://api.github.com/users/seung-lab/orgs","repos_url":"https://api.github.com/users/seung-lab/repos","events_url":"https://api.github.com/users/seung-lab/events{/privacy}","received_events_url":"https://api.github.com/users/seung-lab/received_events","type":"Organization","site_admin":false},"repo":{"id":76393283,"name":"neuroglancer","full_name":"seung-lab/neuroglancer","owner":{"login":"seung-lab","id":796718,"avatar_url":"https://avatars0.githubusercontent.com/u/796718?v=4","gravatar_id":"","url":"https://api.github.com/users/seung-lab","html_url":"https://github.com/seung-lab","followers_url":"https://api.github.com/users/seung-lab/followers","following_url":"https://api.github.com/users/seung-lab/following{/other_user}","gists_url":"https://api.github.com/users/seung-lab/gists{/gist_id}","starred_url":"https://api.github.com/users/seung-lab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seung-lab/subscriptions","organizations_url":"https://api.github.com/users/seung-lab/orgs","repos_url":"https://api.github.com/users/seung-lab/repos","events_url":"https://api.github.com/users/seung-lab/events{/privacy}","received_events_url":"https://api.github.com/users/seung-lab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/seung-lab/neuroglancer","description":"WebGL-based viewer for volumetric data","fork":true,"url":"https://api.github.com/repos/seung-lab/neuroglancer","forks_url":"https://api.github.com/repos/seung-lab/neuroglancer/forks","keys_url":"https://api.github.com/repos/seung-lab/neuroglancer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/seung-lab/neuroglancer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/seung-lab/neuroglancer/teams","hooks_url":"https://api.github.com/repos/seung-lab/neuroglancer/hooks","issue_events_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues/events{/number}","events_url":"https://api.github.com/repos/seung-lab/neuroglancer/events","assignees_url":"https://api.github.com/repos/seung-lab/neuroglancer/assignees{/user}","branches_url":"https://api.github.com/repos/seung-lab/neuroglancer/branches{/branch}","tags_url":"https://api.github.com/repos/seung-lab/neuroglancer/tags","blobs_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/refs{/sha}","trees_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/seung-lab/neuroglancer/statuses/{sha}","languages_url":"https://api.github.com/repos/seung-lab/neuroglancer/languages","stargazers_url":"https://api.github.com/repos/seung-lab/neuroglancer/stargazers","contributors_url":"https://api.github.com/repos/seung-lab/neuroglancer/contributors","subscribers_url":"https://api.github.com/repos/seung-lab/neuroglancer/subscribers","subscription_url":"https://api.github.com/repos/seung-lab/neuroglancer/subscription","commits_url":"https://api.github.com/repos/seung-lab/neuroglancer/commits{/sha}","git_commits_url":"https://api.github.com/repos/seung-lab/neuroglancer/git/commits{/sha}","comments_url":"https://api.github.com/repos/seung-lab/neuroglancer/comments{/number}","issue_comment_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues/comments{/number}","contents_url":"https://api.github.com/repos/seung-lab/neuroglancer/contents/{+path}","compare_url":"https://api.github.com/repos/seung-lab/neuroglancer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/seung-lab/neuroglancer/merges","archive_url":"https://api.github.com/repos/seung-lab/neuroglancer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/seung-lab/neuroglancer/downloads","issues_url":"https://api.github.com/repos/seung-lab/neuroglancer/issues{/number}","pulls_url":"https://api.github.com/repos/seung-lab/neuroglancer/pulls{/number}","milestones_url":"https://api.github.com/repos/seung-lab/neuroglancer/milestones{/number}","notifications_url":"https://api.github.com/repos/seung-lab/neuroglancer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/seung-lab/neuroglancer/labels{/name}","releases_url":"https://api.github.com/repos/seung-lab/neuroglancer/releases{/id}","deployments_url":"https://api.github.com/repos/seung-lab/neuroglancer/deployments","created_at":"2016-12-13T19:54:36Z","updated_at":"2017-07-07T14:51:43Z","pushed_at":"2017-07-18T20:34:11Z","git_url":"git://github.com/seung-lab/neuroglancer.git","ssh_url":"git@github.com:seung-lab/neuroglancer.git","clone_url":"https://github.com/seung-lab/neuroglancer.git","svn_url":"https://github.com/seung-lab/neuroglancer","homepage":"https://neuroglancer-demo.appspot.com","size":7141,"stargazers_count":3,"watchers_count":3,"language":"TypeScript","has_issues":true,"has_projects":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":78,"forks":1,"open_issues":78,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153"},"html":{"href":"https://github.com/seung-lab/neuroglancer/pull/153"},"issue":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/issues/153"},"comments":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/issues/153/comments"},"review_comments":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153/comments"},"review_comment":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/pulls/153/commits"},"statuses":{"href":"https://api.github.com/repos/seung-lab/neuroglancer/statuses/27502fd9e6a46444e22f090a3a49785c860cc5c7"}}}}
{"actor":{"display_login":"nkemnitz"}}