-
Notifications
You must be signed in to change notification settings - Fork 379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System comments #2032
System comments #2032
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"rev_st_changed": "%author% changed review status from <b class=\"old-review-status\">{0}</b> to <b class=\"new-review-status\">{1}</b> %date%", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to store the whole text, couldn't we create some more dense representation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are not store the whole text in the database we just store the type of the system comment and the arguments: |
||
"rev_st_changed_msg": "%author% changed review status from <b class=\"old-review-status\">{0}</b> to <b class=\"new-review-status\">{1}</b> with <i>{2}</i> message %date%", | ||
"comment_changed": "%author% changed comment message from <b class=\"old-comment-message\">{0}</b> to <b class=\"new-comment-message\">{1}</b> %date%" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
import io | ||
import os | ||
import re | ||
import shlex | ||
import tempfile | ||
import zipfile | ||
import zlib | ||
|
@@ -40,6 +41,8 @@ | |
from codechecker_common.logger import get_logger | ||
from codechecker_common.report import get_report_path_hash | ||
|
||
from codechecker_web.shared import webserver_context | ||
|
||
from codechecker_server.profiler import timeit | ||
|
||
from .. import permissions | ||
|
@@ -62,6 +65,27 @@ | |
LOG = get_logger('server') | ||
|
||
|
||
class CommentKindValue(object): | ||
USER = 0 | ||
SYSTEM = 1 | ||
|
||
|
||
def comment_kind_from_thrift_type(kind): | ||
""" Convert the given comment kind from Thrift type to Python enum. """ | ||
if kind == ttypes.CommentKind.USER: | ||
return CommentKindValue.USER | ||
elif kind == ttypes.CommentKind.SYSTEM: | ||
return CommentKindValue.SYSTEM | ||
|
||
|
||
def comment_kind_to_thrift_type(kind): | ||
""" Convert the given comment kind from Python enum to Thrift type. """ | ||
if kind == CommentKindValue.USER: | ||
return ttypes.CommentKind.USER | ||
elif kind == CommentKindValue.SYSTEM: | ||
return ttypes.CommentKind.SYSTEM | ||
|
||
|
||
def verify_limit_range(limit): | ||
"""Verify limit value for the queries. | ||
|
||
|
@@ -653,6 +677,17 @@ def sort_run_data_query(query, sort_mode): | |
return query | ||
|
||
|
||
def escape_whitespaces(s, whitespaces=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add function docstring. |
||
if not whitespaces: | ||
whitespaces = [' ', '\n', '\t', '\r'] | ||
|
||
escaped = s | ||
for w in whitespaces: | ||
escaped = escaped.replace(w, '\\{0}'.format(w)) | ||
|
||
return escaped | ||
|
||
|
||
class ThriftRequestHandler(object): | ||
""" | ||
Connect to database and handle thrift client requests. | ||
|
@@ -736,7 +771,15 @@ def __get_run_ids_to_query(session, cmp_data=None): | |
|
||
return run_ids | ||
|
||
@exc_to_thrift_reqfail | ||
def __add_comment(self, bug_id, message, kind=CommentKindValue.USER): | ||
""" Creates a new comment object. """ | ||
user = self.__get_username() | ||
return Comment(bug_id, | ||
user, | ||
message, | ||
kind, | ||
datetime.now()) | ||
|
||
@timeit | ||
def getRunData(self, run_filter, limit, offset, sort_mode): | ||
self.__require_access() | ||
|
@@ -1331,12 +1374,29 @@ def _setReviewStatus(self, report_id, status, message, session): | |
|
||
user = self.__get_username() | ||
|
||
old_status = review_status.status if review_status.status \ | ||
else review_status_str(ttypes.ReviewStatus.UNREVIEWED) | ||
|
||
review_status.status = review_status_str(status) | ||
review_status.author = user | ||
review_status.message = message.encode('utf8') | ||
review_status.date = datetime.now() | ||
|
||
session.add(review_status) | ||
|
||
if message: | ||
system_comment_msg = 'rev_st_changed_msg {0} {1} {2}'.format( | ||
escape_whitespaces(old_status.capitalize()), | ||
escape_whitespaces(review_status.status.capitalize()), | ||
escape_whitespaces(message)) | ||
else: | ||
system_comment_msg = 'rev_st_changed {0} {1}'.format( | ||
escape_whitespaces(old_status.capitalize()), | ||
escape_whitespaces(review_status.status.capitalize())) | ||
|
||
system_comment = self.__add_comment(review_status.bug_hash, | ||
system_comment_msg, | ||
CommentKindValue.SYSTEM) | ||
session.add(system_comment) | ||
session.flush() | ||
|
||
return True | ||
|
@@ -1397,12 +1457,28 @@ def getComments(self, report_id): | |
.order_by(Comment.created_at.desc()) \ | ||
.all() | ||
|
||
context = webserver_context.get_context() | ||
for comment in comments: | ||
message = comment.message | ||
|
||
sys_comment = comment_kind_from_thrift_type( | ||
ttypes.CommentKind.SYSTEM) | ||
if comment.kind == sys_comment: | ||
elements = shlex.split(message) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need the shlex.split here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before we store a system comment in the database, we will escape the white spaces ( |
||
system_comment = context.system_comment_map.get( | ||
elements[0]) | ||
if system_comment: | ||
for idx, value in enumerate(elements[1:]): | ||
system_comment = system_comment.replace( | ||
'{' + str(idx) + '}', value) | ||
message = system_comment | ||
|
||
result.append(CommentData( | ||
comment.id, | ||
comment.author, | ||
comment.message, | ||
str(comment.created_at))) | ||
message, | ||
str(comment.created_at), | ||
comment_kind_to_thrift_type(comment.kind))) | ||
|
||
return result | ||
else: | ||
|
@@ -1433,19 +1509,19 @@ def getCommentCount(self, report_id): | |
@exc_to_thrift_reqfail | ||
@timeit | ||
def addComment(self, report_id, comment_data): | ||
""" | ||
Add new comment for the given bug. | ||
""" | ||
""" Add new comment for the given bug. """ | ||
self.__require_access() | ||
|
||
if not comment_data.message.strip(): | ||
raise codechecker_api_shared.ttypes.RequestFailed( | ||
codechecker_api_shared.ttypes.ErrorCode.GENERAL, | ||
'The comment message can not be empty!') | ||
|
||
with DBSession(self.__Session) as session: | ||
report = session.query(Report).get(report_id) | ||
if report: | ||
user = self.__get_username() | ||
comment = Comment(report.bug_id, | ||
user, | ||
comment_data.message, | ||
datetime.now()) | ||
|
||
comment = self.__add_comment(report.bug_id, | ||
comment_data.message) | ||
session.add(comment) | ||
session.commit() | ||
|
||
|
@@ -1466,6 +1542,12 @@ def updateComment(self, comment_id, content): | |
Anyonymous comments that can be updated by anybody. | ||
""" | ||
self.__require_access() | ||
|
||
if not content.strip(): | ||
raise codechecker_api_shared.ttypes.RequestFailed( | ||
codechecker_api_shared.ttypes.ErrorCode.GENERAL, | ||
'The comment message can not be empty!') | ||
|
||
with DBSession(self.__Session) as session: | ||
|
||
user = self.__get_username() | ||
|
@@ -1476,8 +1558,22 @@ def updateComment(self, comment_id, content): | |
raise codechecker_api_shared.ttypes.RequestFailed( | ||
codechecker_api_shared.ttypes.ErrorCode.UNAUTHORIZED, | ||
'Unathorized comment modification!') | ||
|
||
# Create system comment if the message is changed. | ||
if comment.message != content: | ||
system_comment_msg = 'comment_changed {0} {1}'.format( | ||
escape_whitespaces(comment.message), | ||
escape_whitespaces(content)) | ||
|
||
system_comment = \ | ||
self.__add_comment(comment.bug_hash, | ||
system_comment_msg, | ||
CommentKindValue.SYSTEM) | ||
session.add(system_comment) | ||
|
||
comment.message = content | ||
session.add(comment) | ||
|
||
session.commit() | ||
return True | ||
else: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -360,12 +360,19 @@ class Comment(Base): | |
bug_hash = Column(String, nullable=False, index=True) | ||
author = Column(String, nullable=False) | ||
message = Column(Binary, nullable=False) | ||
|
||
# Default value is 0 which means a user given comment. | ||
kind = Column(Integer, | ||
nullable=False, | ||
server_default="0") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a comment about the default value here would be nice. '0' means a comment type by the user tight? |
||
|
||
created_at = Column(DateTime, nullable=False) | ||
|
||
def __init__(self, bug_hash, author, message, created_at): | ||
def __init__(self, bug_hash, author, message, kind, created_at): | ||
self.bug_hash = bug_hash | ||
self.author = author | ||
self.message = message | ||
self.kind = kind | ||
self.created_at = created_at | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
"""System comments | ||
|
||
Revision ID: 6cb6a3a41967 | ||
Revises: 3e91d0612422 | ||
Create Date: 2019-04-02 16:12:46.794131 | ||
|
||
""" | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = '6cb6a3a41967' | ||
down_revision = '3e91d0612422' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
def upgrade(): | ||
op.add_column('comments', sa.Column('kind', | ||
sa.Integer(), | ||
server_default="0", | ||
nullable=False),) | ||
|
||
|
||
def downgrade(): | ||
op.drop_column('comments', 'kind') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking maybe it would be better if we introduce some prefix for the thrift types.
There are conversion functions which convert from CommentKind to CommentKind one of them is a thrift enum and the other is a local enum it is hard to follow which type is used where and what is converted to what.