--- 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 )