{"action":"created","comment":{"url":"https://api.github.com/repos/grimoirelab/perceval/pulls/comments/47946849","id":47946849,"diff_hunk":"@@ -0,0 +1,297 @@\n+# -*- coding: utf-8 -*-\n+#\n+# Copyright (C) 2015 Bitergia\n+#\n+# This program is free software; you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation; either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+# This program is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with this program; if not, write to the Free Software\n+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n+#\n+# Authors:\n+#   Alvaro del Castillo San Felix <acs@bitergia.com>\n+#\n+\n+'''Gerrit backend for Perseval'''\n+\n+\n+from datetime import datetime\n+import json\n+import logging\n+import os.path\n+import re\n+import subprocess\n+from time import time\n+\n+from ..backend import Backend, BackendCommand\n+from ..cache import Cache\n+from ..errors import BackendError, CacheError\n+from ..utils import DEFAULT_DATETIME, str_to_datetime\n+\n+\n+class Gerrit(Backend):\n+\n+    name = \"gerrit\"\n+\n+    def __init__(self, user=None, url=None, nreviews=None,\n+                 cache=None, **nouse):\n+        super().__init__(cache=cache)\n+        self.repository = url\n+        self.nreviews = nreviews\n+        self.last_review = None  # last review processed\n+        self.number_results = None  # last number of results\n+        self.client = GerritClient(self.repository, user, nreviews)\n+\n+    def fetch_from_cache(self):\n+        \"\"\"Fetch the bugs from the cache.\n+\n+        It returns the bugs stored in the cache object provided during\n+        the initialization of the object. If this method is called but\n+        no cache object was provided, the method will raise a `CacheError`\n+        exception.\n+\n+        :returns: a generator of items\n+\n+        :raises CacheError: raised when an error occurs accesing the\n+            cache\n+        \"\"\"\n+        if not self.cache:\n+            raise CacheError(cause=\"cache instance was not provided\")\n+\n+        cache_items = self.cache.retrieve()\n+\n+        while True:\n+            try:\n+                raw_items = next(cache_items)\n+                if raw_items is None:\n+                    break\n+                reviews = self._parse_reviews(raw_items)\n+            except StopIteration:\n+                break\n+\n+            for item in reviews:\n+                yield item\n+\n+    def fetch(self, from_date=DEFAULT_DATETIME):\n+        self._purge_cache_queue()\n+\n+        self.last_item = None\n+        reviews = self._get_reviews()\n+\n+        while reviews:\n+            review = reviews.pop(0)\n+            updated = datetime.fromtimestamp(review['lastUpdated'])\n+            if updated <= from_date:\n+                logging.debug(\"No more updates for %s\" % (self.repository))\n+                break\n+\n+            yield review\n+\n+            if not reviews:\n+                reviews = self._get_reviews()\n+\n+    def _get_reviews(self):\n+        reviews = []\n+\n+        if self.number_results and self.number_results < self.nreviews:\n+            # No more reviews\n+            return reviews\n+\n+        task_init = time()\n+        raw_data = self.client.get_items(self.last_item)\n+        self._push_cache_queue(raw_data)\n+        self._flush_cache_queue()\n+        reviews = self._parse_reviews(raw_data)\n+        self.last_item = self.client.get_next_item(self.last_item, reviews[-1])\n+        logging.info(\"Received %i reviews in %.2fs\" % (len(reviews),\n+                                                       time()-task_init))\n+        return reviews\n+\n+    def _parse_reviews(self, raw_data):\n+        # Join isolated reviews in JSON in array for parsing\n+        items_raw = \"[\" + raw_data.replace(\"\\n\", \",\") + \"]\"\n+        items_raw = items_raw.replace(\",]\", \"]\")\n+        items = json.loads(items_raw)\n+        reviews = []\n+\n+        for item in items:\n+            if 'rowCount' in item.keys():\n+                # stats data, not a review\n+                self.number_results = item['rowCount']\n+            elif 'project' in item.keys():\n+                reviews.append(item)\n+\n+        return reviews\n+\n+\n+class GerritClient():\n+\n+    def __init__(self, repository, user, nreviews):\n+        self.gerrit_user = user\n+        self.nreviews = nreviews\n+        self.repository = repository\n+        self.project = None\n+        self.version = None\n+        self.gerrit_cmd = \"ssh -p 29418 %s@%s\" % (self.gerrit_user,\n+                                                  self.repository)\n+        self.gerrit_cmd += \" gerrit \"","path":"perceval/backends/gerrit.py","position":145,"original_position":145,"commit_id":"785049e0edd4d1e03723da4d165db5a354af1900","original_commit_id":"785049e0edd4d1e03723da4d165db5a354af1900","user":{"login":"acs","id":209533,"avatar_url":"https://avatars.githubusercontent.com/u/209533?v=3","gravatar_id":"","url":"https://api.github.com/users/acs","html_url":"https://github.com/acs","followers_url":"https://api.github.com/users/acs/followers","following_url":"https://api.github.com/users/acs/following{/other_user}","gists_url":"https://api.github.com/users/acs/gists{/gist_id}","starred_url":"https://api.github.com/users/acs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/acs/subscriptions","organizations_url":"https://api.github.com/users/acs/orgs","repos_url":"https://api.github.com/users/acs/repos","events_url":"https://api.github.com/users/acs/events{/privacy}","received_events_url":"https://api.github.com/users/acs/received_events","type":"User","site_admin":false},"body":"Done!","created_at":"2015-12-17T19:07:59Z","updated_at":"2015-12-17T19:07:59Z","html_url":"https://github.com/grimoirelab/perceval/pull/1#discussion_r47946849","pull_request_url":"https://api.github.com/repos/grimoirelab/perceval/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/grimoirelab/perceval/pulls/comments/47946849"},"html":{"href":"https://github.com/grimoirelab/perceval/pull/1#discussion_r47946849"},"pull_request":{"href":"https://api.github.com/repos/grimoirelab/perceval/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/grimoirelab/perceval/pulls/1","id":52699579,"html_url":"https://github.com/grimoirelab/perceval/pull/1","diff_url":"https://github.com/grimoirelab/perceval/pull/1.diff","patch_url":"https://github.com/grimoirelab/perceval/pull/1.patch","issue_url":"https://api.github.com/repos/grimoirelab/perceval/issues/1","number":1,"state":"open","locked":false,"title":"[gerrit] Add backend implementation.","user":{"login":"acs","id":209533,"avatar_url":"https://avatars.githubusercontent.com/u/209533?v=3","gravatar_id":"","url":"https://api.github.com/users/acs","html_url":"https://github.com/acs","followers_url":"https://api.github.com/users/acs/followers","following_url":"https://api.github.com/users/acs/following{/other_user}","gists_url":"https://api.github.com/users/acs/gists{/gist_id}","starred_url":"https://api.github.com/users/acs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/acs/subscriptions","organizations_url":"https://api.github.com/users/acs/orgs","repos_url":"https://api.github.com/users/acs/repos","events_url":"https://api.github.com/users/acs/events{/privacy}","received_events_url":"https://api.github.com/users/acs/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-12-04T17:48:07Z","updated_at":"2015-12-17T19:07:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a034a0f99bc5b23aed3e24c3c911fcef7088b49a","assignee":{"login":"sduenas","id":833352,"avatar_url":"https://avatars.githubusercontent.com/u/833352?v=3","gravatar_id":"","url":"https://api.github.com/users/sduenas","html_url":"https://github.com/sduenas","followers_url":"https://api.github.com/users/sduenas/followers","following_url":"https://api.github.com/users/sduenas/following{/other_user}","gists_url":"https://api.github.com/users/sduenas/gists{/gist_id}","starred_url":"https://api.github.com/users/sduenas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sduenas/subscriptions","organizations_url":"https://api.github.com/users/sduenas/orgs","repos_url":"https://api.github.com/users/sduenas/repos","events_url":"https://api.github.com/users/sduenas/events{/privacy}","received_events_url":"https://api.github.com/users/sduenas/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/grimoirelab/perceval/pulls/1/commits","review_comments_url":"https://api.github.com/repos/grimoirelab/perceval/pulls/1/comments","review_comment_url":"https://api.github.com/repos/grimoirelab/perceval/pulls/comments{/number}","comments_url":"https://api.github.com/repos/grimoirelab/perceval/issues/1/comments","statuses_url":"https://api.github.com/repos/grimoirelab/perceval/statuses/785049e0edd4d1e03723da4d165db5a354af1900","head":{"label":"acs:gerrit","ref":"gerrit","sha":"785049e0edd4d1e03723da4d165db5a354af1900","user":{"login":"acs","id":209533,"avatar_url":"https://avatars.githubusercontent.com/u/209533?v=3","gravatar_id":"","url":"https://api.github.com/users/acs","html_url":"https://github.com/acs","followers_url":"https://api.github.com/users/acs/followers","following_url":"https://api.github.com/users/acs/following{/other_user}","gists_url":"https://api.github.com/users/acs/gists{/gist_id}","starred_url":"https://api.github.com/users/acs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/acs/subscriptions","organizations_url":"https://api.github.com/users/acs/orgs","repos_url":"https://api.github.com/users/acs/repos","events_url":"https://api.github.com/users/acs/events{/privacy}","received_events_url":"https://api.github.com/users/acs/received_events","type":"User","site_admin":false},"repo":{"id":47419452,"name":"perceval-1","full_name":"acs/perceval-1","owner":{"login":"acs","id":209533,"avatar_url":"https://avatars.githubusercontent.com/u/209533?v=3","gravatar_id":"","url":"https://api.github.com/users/acs","html_url":"https://github.com/acs","followers_url":"https://api.github.com/users/acs/followers","following_url":"https://api.github.com/users/acs/following{/other_user}","gists_url":"https://api.github.com/users/acs/gists{/gist_id}","starred_url":"https://api.github.com/users/acs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/acs/subscriptions","organizations_url":"https://api.github.com/users/acs/orgs","repos_url":"https://api.github.com/users/acs/repos","events_url":"https://api.github.com/users/acs/events{/privacy}","received_events_url":"https://api.github.com/users/acs/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/acs/perceval-1","description":"Send Sir Perceval on a quest to retrieve and gather data from software repositories.","fork":true,"url":"https://api.github.com/repos/acs/perceval-1","forks_url":"https://api.github.com/repos/acs/perceval-1/forks","keys_url":"https://api.github.com/repos/acs/perceval-1/keys{/key_id}","collaborators_url":"https://api.github.com/repos/acs/perceval-1/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/acs/perceval-1/teams","hooks_url":"https://api.github.com/repos/acs/perceval-1/hooks","issue_events_url":"https://api.github.com/repos/acs/perceval-1/issues/events{/number}","events_url":"https://api.github.com/repos/acs/perceval-1/events","assignees_url":"https://api.github.com/repos/acs/perceval-1/assignees{/user}","branches_url":"https://api.github.com/repos/acs/perceval-1/branches{/branch}","tags_url":"https://api.github.com/repos/acs/perceval-1/tags","blobs_url":"https://api.github.com/repos/acs/perceval-1/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/acs/perceval-1/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/acs/perceval-1/git/refs{/sha}","trees_url":"https://api.github.com/repos/acs/perceval-1/git/trees{/sha}","statuses_url":"https://api.github.com/repos/acs/perceval-1/statuses/{sha}","languages_url":"https://api.github.com/repos/acs/perceval-1/languages","stargazers_url":"https://api.github.com/repos/acs/perceval-1/stargazers","contributors_url":"https://api.github.com/repos/acs/perceval-1/contributors","subscribers_url":"https://api.github.com/repos/acs/perceval-1/subscribers","subscription_url":"https://api.github.com/repos/acs/perceval-1/subscription","commits_url":"https://api.github.com/repos/acs/perceval-1/commits{/sha}","git_commits_url":"https://api.github.com/repos/acs/perceval-1/git/commits{/sha}","comments_url":"https://api.github.com/repos/acs/perceval-1/comments{/number}","issue_comment_url":"https://api.github.com/repos/acs/perceval-1/issues/comments{/number}","contents_url":"https://api.github.com/repos/acs/perceval-1/contents/{+path}","compare_url":"https://api.github.com/repos/acs/perceval-1/compare/{base}...{head}","merges_url":"https://api.github.com/repos/acs/perceval-1/merges","archive_url":"https://api.github.com/repos/acs/perceval-1/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/acs/perceval-1/downloads","issues_url":"https://api.github.com/repos/acs/perceval-1/issues{/number}","pulls_url":"https://api.github.com/repos/acs/perceval-1/pulls{/number}","milestones_url":"https://api.github.com/repos/acs/perceval-1/milestones{/number}","notifications_url":"https://api.github.com/repos/acs/perceval-1/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/acs/perceval-1/labels{/name}","releases_url":"https://api.github.com/repos/acs/perceval-1/releases{/id}","created_at":"2015-12-04T17:36:17Z","updated_at":"2015-12-04T17:36:18Z","pushed_at":"2015-12-05T06:00:43Z","git_url":"git://github.com/acs/perceval-1.git","ssh_url":"git@github.com:acs/perceval-1.git","clone_url":"https://github.com/acs/perceval-1.git","svn_url":"https://github.com/acs/perceval-1","homepage":null,"size":82,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"grimoirelab:master","ref":"master","sha":"ad6252c1e6d1b101350d5c94b23900a38778d219","user":{"login":"grimoirelab","id":16151805,"avatar_url":"https://avatars.githubusercontent.com/u/16151805?v=3","gravatar_id":"","url":"https://api.github.com/users/grimoirelab","html_url":"https://github.com/grimoirelab","followers_url":"https://api.github.com/users/grimoirelab/followers","following_url":"https://api.github.com/users/grimoirelab/following{/other_user}","gists_url":"https://api.github.com/users/grimoirelab/gists{/gist_id}","starred_url":"https://api.github.com/users/grimoirelab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grimoirelab/subscriptions","organizations_url":"https://api.github.com/users/grimoirelab/orgs","repos_url":"https://api.github.com/users/grimoirelab/repos","events_url":"https://api.github.com/users/grimoirelab/events{/privacy}","received_events_url":"https://api.github.com/users/grimoirelab/received_events","type":"Organization","site_admin":false},"repo":{"id":47415120,"name":"perceval","full_name":"grimoirelab/perceval","owner":{"login":"grimoirelab","id":16151805,"avatar_url":"https://avatars.githubusercontent.com/u/16151805?v=3","gravatar_id":"","url":"https://api.github.com/users/grimoirelab","html_url":"https://github.com/grimoirelab","followers_url":"https://api.github.com/users/grimoirelab/followers","following_url":"https://api.github.com/users/grimoirelab/following{/other_user}","gists_url":"https://api.github.com/users/grimoirelab/gists{/gist_id}","starred_url":"https://api.github.com/users/grimoirelab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grimoirelab/subscriptions","organizations_url":"https://api.github.com/users/grimoirelab/orgs","repos_url":"https://api.github.com/users/grimoirelab/repos","events_url":"https://api.github.com/users/grimoirelab/events{/privacy}","received_events_url":"https://api.github.com/users/grimoirelab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/grimoirelab/perceval","description":"Send Sir Perceval on a quest to retrieve and gather data from software repositories.","fork":false,"url":"https://api.github.com/repos/grimoirelab/perceval","forks_url":"https://api.github.com/repos/grimoirelab/perceval/forks","keys_url":"https://api.github.com/repos/grimoirelab/perceval/keys{/key_id}","collaborators_url":"https://api.github.com/repos/grimoirelab/perceval/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/grimoirelab/perceval/teams","hooks_url":"https://api.github.com/repos/grimoirelab/perceval/hooks","issue_events_url":"https://api.github.com/repos/grimoirelab/perceval/issues/events{/number}","events_url":"https://api.github.com/repos/grimoirelab/perceval/events","assignees_url":"https://api.github.com/repos/grimoirelab/perceval/assignees{/user}","branches_url":"https://api.github.com/repos/grimoirelab/perceval/branches{/branch}","tags_url":"https://api.github.com/repos/grimoirelab/perceval/tags","blobs_url":"https://api.github.com/repos/grimoirelab/perceval/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/grimoirelab/perceval/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/grimoirelab/perceval/git/refs{/sha}","trees_url":"https://api.github.com/repos/grimoirelab/perceval/git/trees{/sha}","statuses_url":"https://api.github.com/repos/grimoirelab/perceval/statuses/{sha}","languages_url":"https://api.github.com/repos/grimoirelab/perceval/languages","stargazers_url":"https://api.github.com/repos/grimoirelab/perceval/stargazers","contributors_url":"https://api.github.com/repos/grimoirelab/perceval/contributors","subscribers_url":"https://api.github.com/repos/grimoirelab/perceval/subscribers","subscription_url":"https://api.github.com/repos/grimoirelab/perceval/subscription","commits_url":"https://api.github.com/repos/grimoirelab/perceval/commits{/sha}","git_commits_url":"https://api.github.com/repos/grimoirelab/perceval/git/commits{/sha}","comments_url":"https://api.github.com/repos/grimoirelab/perceval/comments{/number}","issue_comment_url":"https://api.github.com/repos/grimoirelab/perceval/issues/comments{/number}","contents_url":"https://api.github.com/repos/grimoirelab/perceval/contents/{+path}","compare_url":"https://api.github.com/repos/grimoirelab/perceval/compare/{base}...{head}","merges_url":"https://api.github.com/repos/grimoirelab/perceval/merges","archive_url":"https://api.github.com/repos/grimoirelab/perceval/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/grimoirelab/perceval/downloads","issues_url":"https://api.github.com/repos/grimoirelab/perceval/issues{/number}","pulls_url":"https://api.github.com/repos/grimoirelab/perceval/pulls{/number}","milestones_url":"https://api.github.com/repos/grimoirelab/perceval/milestones{/number}","notifications_url":"https://api.github.com/repos/grimoirelab/perceval/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/grimoirelab/perceval/labels{/name}","releases_url":"https://api.github.com/repos/grimoirelab/perceval/releases{/id}","created_at":"2015-12-04T16:20:11Z","updated_at":"2015-12-04T17:35:53Z","pushed_at":"2015-12-05T06:00:44Z","git_url":"git://github.com/grimoirelab/perceval.git","ssh_url":"git@github.com:grimoirelab/perceval.git","clone_url":"https://github.com/grimoirelab/perceval.git","svn_url":"https://github.com/grimoirelab/perceval","homepage":null,"size":64,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":2,"forks":1,"open_issues":2,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/grimoirelab/perceval/pulls/1"},"html":{"href":"https://github.com/grimoirelab/perceval/pull/1"},"issue":{"href":"https://api.github.com/repos/grimoirelab/perceval/issues/1"},"comments":{"href":"https://api.github.com/repos/grimoirelab/perceval/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/grimoirelab/perceval/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/grimoirelab/perceval/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/grimoirelab/perceval/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/grimoirelab/perceval/statuses/785049e0edd4d1e03723da4d165db5a354af1900"}}}}