changeset 7489:445d6875c2ee

model: comments: allow selective retrieval of inline comments Allow retrieving inline comments of only a specified file and possibly line number, by filtering at database level. This will be useful when adding more code context in comment notification emails.
author Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
date Wed, 26 Dec 2018 21:39:32 +0100
parents 6952e88f8ded
children 14e8dcffd279
files kallithea/model/comment.py kallithea/tests/models/test_comments.py
diffstat 2 files changed, 103 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/model/comment.py	Wed Dec 26 20:52:04 2018 +0100
+++ b/kallithea/model/comment.py	Wed Dec 26 21:39:32 2018 +0100
@@ -244,30 +244,51 @@
         return self._get_comments(repo_id, revision=revision, pull_request=pull_request,
                                   inline=False)
 
-    def get_inline_comments(self, repo_id, revision=None, pull_request=None):
+    def get_inline_comments(self, repo_id, revision=None, pull_request=None,
+                f_path=None, line_no=None):
         """
         Gets inline comments for either revision or pull_request.
 
         Returns a list of tuples with file path and list of comments per line number.
         """
         comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request,
-                                      inline=True)
+                                      inline=True, f_path=f_path, line_no=line_no)
 
         paths = defaultdict(lambda: defaultdict(list))
         for co in comments:
             paths[co.f_path][co.line_no].append(co)
         return paths.items()
 
-    def _get_comments(self, repo_id, revision=None, pull_request=None, inline=False):
+    def _get_comments(self, repo_id, revision=None, pull_request=None,
+                inline=False, f_path=None, line_no=None):
         """
         Gets comments for either revision or pull_request_id, either inline or general.
+        If a file path and optionally line number are given, return only the matching inline comments.
         """
+        if f_path is None and line_no is not None:
+            raise Exception("line_no only makes sense if f_path is given.")
+
+        if inline is None and f_path is not None:
+            raise Exception("f_path only makes sense for inline comments.")
+
         q = Session().query(ChangesetComment)
 
         if inline:
-            q = q.filter(ChangesetComment.line_no != None) \
-                .filter(ChangesetComment.f_path != None)
+            if f_path is not None:
+                # inline comments for a given file...
+                q = q.filter(ChangesetComment.f_path == f_path)
+                if line_no is None:
+                    # ... on any line
+                    q = q.filter(ChangesetComment.line_no != None)
+                else:
+                    # ... on specific line
+                    q = q.filter(ChangesetComment.line_no == line_no)
+            else:
+                # all inline comments
+                q = q.filter(ChangesetComment.line_no != None) \
+                    .filter(ChangesetComment.f_path != None)
         else:
+            # all general comments
             q = q.filter(ChangesetComment.line_no == None) \
                 .filter(ChangesetComment.f_path == None)
 
--- a/kallithea/tests/models/test_comments.py	Wed Dec 26 20:52:04 2018 +0100
+++ b/kallithea/tests/models/test_comments.py	Wed Dec 26 21:39:32 2018 +0100
@@ -2,16 +2,20 @@
 from kallithea.model.comment import ChangesetCommentsModel
 from kallithea.model.db import Repository
 
+import pytest
 from tg.util.webtest import test_context
 
 class TestComments(TestController):
 
-    def _check_comment_count(self, repo_id, revision, expected_len_comments, expected_len_inline_comments):
+    def _check_comment_count(self, repo_id, revision,
+            expected_len_comments, expected_len_inline_comments,
+            f_path=None, line_no=None
+    ):
         comments = ChangesetCommentsModel().get_comments(repo_id,
                 revision=revision)
         assert len(comments) == expected_len_comments
         inline_comments = ChangesetCommentsModel().get_inline_comments(repo_id,
-                revision=revision)
+                revision=revision, f_path=f_path, line_no=line_no)
         assert len(inline_comments) == expected_len_inline_comments
 
         return comments, inline_comments
@@ -153,3 +157,74 @@
 
             self._check_comment_count(repo_id, revision,
                     expected_len_comments=0, expected_len_inline_comments=0)
+
+    def test_selective_retrieval_of_inline_comments(self):
+        with test_context(self.app):
+            repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
+            revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
+
+            self._check_comment_count(repo_id, revision,
+                    expected_len_comments=0, expected_len_inline_comments=0)
+
+            text = u'an inline comment'
+            f_path = u'vcs/tests/base.py'
+            line_no = u'n50'
+            new_comment = ChangesetCommentsModel().create(
+                    text=text,
+                    repo=HG_REPO,
+                    author=TEST_USER_REGULAR_LOGIN,
+                    revision=revision,
+                    f_path=f_path,
+                    line_no=line_no,
+                    send_email=False)
+
+            text2 = u'another inline comment, same file'
+            line_no2 = u'o41'
+            new_comment2 = ChangesetCommentsModel().create(
+                    text=text2,
+                    repo=HG_REPO,
+                    author=TEST_USER_REGULAR_LOGIN,
+                    revision=revision,
+                    f_path=f_path,
+                    line_no=line_no2,
+                    send_email=False)
+
+            text3 = u'another inline comment, same file'
+            f_path3 = u'vcs/tests/test_hg.py'
+            line_no3 = u'n159'
+            new_comment3 = ChangesetCommentsModel().create(
+                    text=text3,
+                    repo=HG_REPO,
+                    author=TEST_USER_REGULAR_LOGIN,
+                    revision=revision,
+                    f_path=f_path3,
+                    line_no=line_no3,
+                    send_email=False)
+
+            # now selectively retrieve comments of one file
+            comments, inline_comments = self._check_comment_count(repo_id, revision,
+                    f_path=f_path,
+                    expected_len_comments=0, expected_len_inline_comments=1)
+            # inline_comments is a list of tuples (file_path, dict)
+            # where the dict keys are line numbers and values are lists of comments
+            assert inline_comments[0][0] == f_path
+            assert len(inline_comments[0][1]) == 2
+            assert inline_comments[0][1][line_no][0].text == text
+            assert inline_comments[0][1][line_no2][0].text == text2
+
+            # now selectively retrieve comments of one file, one line
+            comments, inline_comments = self._check_comment_count(repo_id, revision,
+                    f_path=f_path, line_no=line_no2,
+                    expected_len_comments=0, expected_len_inline_comments=1)
+            # inline_comments is a list of tuples (file_path, dict)
+            # where the dict keys are line numbers and values are lists of comments
+            assert inline_comments[0][0] == f_path
+            assert len(inline_comments[0][1]) == 1
+            assert inline_comments[0][1][line_no2][0].text == text2
+
+            # verify that retrieval based on line_no but no f_path fails
+            with pytest.raises(Exception) as excinfo:
+                self._check_comment_count(repo_id, revision,
+                        f_path=None, line_no=line_no2,
+                        expected_len_comments=0, expected_len_inline_comments=0)
+            assert 'line_no only makes sense if f_path is given' in str(excinfo.value)