# HG changeset patch # User Thomas De Schampheleire # Date 2018-12-26 20:39:32 # Node ID 445d6875c2eefdd8d10676e761cfe1e82581f78a # Parent 6952e88f8ded672dafd354a37291278e74ef53ef 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. diff --git a/kallithea/model/comment.py b/kallithea/model/comment.py --- a/kallithea/model/comment.py +++ b/kallithea/model/comment.py @@ -244,30 +244,51 @@ class ChangesetCommentsModel(object): 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) diff --git a/kallithea/tests/models/test_comments.py b/kallithea/tests/models/test_comments.py --- a/kallithea/tests/models/test_comments.py +++ b/kallithea/tests/models/test_comments.py @@ -2,16 +2,20 @@ from kallithea.tests.base import * 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 @@ class TestComments(TestController): 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)