# HG changeset patch # User Steve Losh # Date 1255208294 14400 # Node ID 1f2f3cb23ac39c70f1b999c02814b3eff8edcf00 # Parent f740af183dcafd8c7f8447291e4ec3e764081742 Add real date and time support to the API, and use it in the UI. diff -r f740af183dca -r 1f2f3cb23ac3 review/api.py --- a/review/api.py Sat Oct 10 11:05:05 2009 -0400 +++ b/review/api.py Sat Oct 10 16:58:14 2009 -0400 @@ -2,12 +2,10 @@ """The API for interacting with code review data.""" -import os +import datetime, operator, os import messages, templates -from datetime import datetime -from mercurial import cmdutil, hg, patch +from mercurial import cmdutil, hg, patch, util from mercurial.node import hex -from mercurial.util import sha1 class PreexistingDatastore(Exception): @@ -98,6 +96,26 @@ data['message'] = message return data +def _datetime_from_hgdate(hgdate): + """Return a datetime.datetime for the given Mecurial-style date tuple. + + It will have NO timezone information -- the date and time are what a clock + next to the current computer would have read at the instant represented + by the Mercurial-style date! + + """ + offset = abs(hgdate[1] - util.makedate()[1]) + later = offset < 0 + offset = datetime.timedelta(seconds=offset) + + conversion_format = '%Y-%m-%d %H:%M:%S' + bare = util.datestr(hgdate, format=conversion_format) + bare_datetime = datetime.datetime.strptime(bare, conversion_format) + if later: + return bare_datetime + offset + else: + return bare_datetime - offset + def sanitize_path(p, repo=None): """Sanitize a (platform-specific) path. @@ -217,12 +235,16 @@ data = _parse_data(self.repo['tip'][fn].data()) data['lines'] = data['lines'].split(',') data['lines'] = map(int, filter(None, data['lines'])) + data['hgdate'] = util.parsedate(data['hgdate']) self.comments.append(ReviewComment(**data)) + self.comments.sort(key=operator.attrgetter('local_datetime')) self.signoffs = [] for fn in signofffns: data = _parse_data(self.repo['tip'][fn].data()) + data['hgdate'] = util.parsedate(data['hgdate']) self.signoffs.append(ReviewSignoff(**data)) + self.signoffs.sort(key=operator.attrgetter('local_datetime')) else: self.comments = [] self.signoffs = [] @@ -253,7 +275,7 @@ raise SignoffExists existing[0]._delete(self.ui, self.repo) - signoff = ReviewSignoff(self.ui.username(), datetime.utcnow(), + signoff = ReviewSignoff(self.ui.username(), util.makedate(), self.node, opinion, message) signoff._commit(self.ui, self.repo) @@ -274,7 +296,7 @@ if filename and filename not in self.target[self.node].files(): raise FileNotInChangeset(filename) - comment = ReviewComment(self.ui.username(), datetime.utcnow(), + comment = ReviewComment(self.ui.username(), util.makedate(), self.node, filename, lines, message) comment._commit(self.ui, self.repo) @@ -402,7 +424,7 @@ os.mkdir(path) data = self._render_data() - filename = sha1(data).hexdigest() + filename = util.sha1(data).hexdigest() objectpath = os.path.join(path, filename) with open(objectpath, 'w') as objectfile: @@ -418,7 +440,7 @@ raise CannotDeleteObject data = self._render_data() - filename = sha1(data).hexdigest() + filename = util.sha1(data).hexdigest() objectpath = os.path.join(repo.root, self.node, self.container, filename) os.remove(objectpath) @@ -426,6 +448,9 @@ cmdutil.commit(ui, repo, _commitfunc, [objectpath], { 'message': self.delete_message % self.node, 'addremove': True, }) + @property + def local_datetime(self): + return _datetime_from_hgdate(self.hgdate) class ReviewComment(_ReviewObject): """A single review comment. @@ -436,17 +461,26 @@ comment = rcset.comments[0] comment.author - comment.datetime + comment.hgdate comment.node comment.filename comment.lines comment.message + comment.local_datetime - Each item is a string, except for datetime (a Python datetime.datetime - object) and lines (a list of ints). + Each item is a string, except for lines, hgdate, and local_datetime. + + lines is a list of ints. + + hgdate is a tuple of (seconds from the epoch, seconds offset from UTC), + which is the format Mercurial itself uses internally. + + local_datetime is a datetime object representing what a clock on the wall + next to the current computer would have read at the instant the comment + was added. """ - def __init__(self, author, datetime, node, filename, lines, message, **extra): + def __init__(self, author, hgdate, node, filename, lines, message, **extra): """Initialize a ReviewComment. You shouldn't need to create these directly -- use a ReviewChangeset @@ -462,7 +496,7 @@ container='comments', commit_message=messages.COMMIT_COMMENT, ) self.author = author - self.datetime = datetime + self.hgdate = hgdate self.node = node self.filename = filename self.lines = lines @@ -471,13 +505,13 @@ def _render_data(self): """Render the data of this comment into a string for writing to disk. - You probably don't need to call this directly, the add_signoff method + You probably don't need to call this directly, the add_comment method of a ReviewChangeset will handle it for you. """ - datetime = str(self.datetime) + rendered_date = util.datestr(self.hgdate) lines = ','.join(self.lines) - return templates.COMMENT_FILE_TEMPLATE % ( self.author, datetime, + return templates.COMMENT_FILE_TEMPLATE % ( self.author, rendered_date, self.node, self.filename, lines, self.message ) def __str__(self): @@ -503,15 +537,22 @@ signoff = rcset.comments[0] signoff.author - signoff.datetime + signoff.hgdate signoff.node signoff.opinion signoff.message - Each item is a string. + Each item is a string, except for hgdate and local_datetime. + + hgdate is a tuple of (seconds from the epoch, seconds offset from UTC), + which is the format Mercurial itself uses internally. + + local_datetime is a datetime object representing what a clock on the wall + next to the current computer would have read at the instant the signoff + was added. """ - def __init__(self, author, datetime, node, opinion, message, **extra): + def __init__(self, author, hgdate, node, opinion, message, **extra): """Initialize a ReviewSignoff. You shouldn't need to create these directly -- use a ReviewChangeset @@ -528,7 +569,7 @@ delete_message=messages.DELETE_SIGNOFF, ) self.author = author - self.datetime = datetime + self.hgdate = hgdate self.node = node self.opinion = opinion self.message = message @@ -540,7 +581,7 @@ of a ReviewChangeset will handle it for you. """ - datetime = str(self.datetime) - return templates.SIGNOFF_FILE_TEMPLATE % ( self.author, datetime, + rendered_date = util.datestr(self.hgdate) + return templates.SIGNOFF_FILE_TEMPLATE % ( self.author, rendered_date, self.node, self.opinion, self.message ) diff -r f740af183dca -r 1f2f3cb23ac3 review/extension_ui.py --- a/review/extension_ui.py Sat Oct 10 11:05:05 2009 -0400 +++ b/review/extension_ui.py Sat Oct 10 16:58:14 2009 -0400 @@ -8,7 +8,7 @@ import operator, os import messages from api import * -from mercurial import util +from mercurial import templatefilters, util from mercurial.node import short @@ -99,9 +99,19 @@ def _print_comment(comment, before='', after=''): ui.write(before) - ui.write(messages.REVIEW_LOG_COMMENT_AUTHOR % comment.author) + + author = templatefilters.person(comment.author) + author_part = messages.REVIEW_LOG_COMMENT_AUTHOR % author + + age = templatefilters.age(comment.hgdate) + age_part = messages.REVIEW_LOG_COMMENT_AGE % age + + spacing = ' ' * (80 - (len(author_part) + len(age_part))) + + ui.write(author_part + spacing + age_part + '\n') for line in comment.message.splitlines(): ui.write(messages.REVIEW_LOG_COMMENT_LINE % line) + ui.write(after) review_level_comments = filter(lambda c: not c.filename, rcset.comments) diff -r f740af183dca -r 1f2f3cb23ac3 review/messages.py --- a/review/messages.py Sat Oct 10 11:05:05 2009 -0400 +++ b/review/messages.py Sat Oct 10 16:58:14 2009 -0400 @@ -71,9 +71,8 @@ REVIEW_LOG_FILE_HEADER = """changes in %s""" -REVIEW_LOG_COMMENT_AUTHOR = """\ -# %s said: -""" +REVIEW_LOG_COMMENT_AUTHOR = """# %s said:""" +REVIEW_LOG_COMMENT_AGE = """(%s ago)""" REVIEW_LOG_COMMENT_LINE = """\ # %s diff -r f740af183dca -r 1f2f3cb23ac3 review/templates.py --- a/review/templates.py Sat Oct 10 11:05:05 2009 -0400 +++ b/review/templates.py Sat Oct 10 16:58:14 2009 -0400 @@ -2,7 +2,7 @@ COMMENT_FILE_TEMPLATE = """\ author:%s -datetime:%s +hgdate:%s node:%s filename:%s lines:%s @@ -11,7 +11,7 @@ SIGNOFF_FILE_TEMPLATE = """\ author:%s -datetime:%s +hgdate:%s node:%s opinion:%s