from elixir import *
from sqlalchemy import func
+### Utility function(s)
+
+def indent_comments(comments):
+ """Given a list of comment objects, returns the same comments (and changes
+ them in-place) with an `indent` property set on each comment. This
+ indicates how deeply nested each comment is, relative to the first comment.
+ The first comment's indent level is 0.
+
+ The comments must be a complete subtree, ordered by their `left` property.
+
+ This function will also cache the `parent` property for each comment.
+ """
+
+ last_comment = None
+ indent = 0
+ right_ancestry = []
+ for comment in comments:
+ # If this comment is a child of the last, bump the nesting level
+ if last_comment and comment.left < last_comment.right:
+ indent = indent + 1
+ # Remember current ancestory relevant to the root
+ right_ancestry.append(last_comment)
+
+ # On the other hand, for every nesting level this comment may have just
+ # broken out of, back out a level
+ for i in xrange(len(right_ancestry) - 1, -1, -1):
+ if comment.left > right_ancestry[i].right:
+ indent = indent - 1
+ right_ancestry.pop(i)
+
+ # Cache parent comment
+ if len(right_ancestry):
+ comment._parent = right_ancestry[-1]
+
+ comment.indent = indent
+
+ last_comment = comment
+
+ return comments
+
+
+### Usual database classes
+
class Discussion(Entity):
"""Represents a collection of comments attached to some other object."""
count = Field(Integer)
self.discussion.count += 1
if parent:
- # Parent comment given. Add this comment just before the parent's
- # right side...
- self.left = parent.right
- self.right = parent.right + 1
-
- # ...then adjust all rightward comments accordingly
+ # Parent comment given.
+ parent_right = parent.right
+ # Shift every left/right ahead by two to make room for the new
+ # comment's left/right
+ Comment.query.filter(Comment.discussion == self.discussion) \
+ .filter(Comment.left >= parent_right) \
+ .update({ Comment.left: Comment.left + 2 })
Comment.query.filter(Comment.discussion == self.discussion) \
- .filter(Comment.right > parent.right) \
- .update({ Comment.left: Comment.left + 2,
- Comment.right: Comment.right + 2 })
+ .filter(Comment.right >= parent_right) \
+ .update({ Comment.right: Comment.right + 2 })
- # And, finally, update the parent's right endpoint
- parent.right += 2
+ # Then stick the new comment in the right place
+ self.left = parent_right
+ self.right = parent_right + 1
else:
query = session.query(func.max(Comment.right)) \
else:
self.left = max_right + 1
self.right = max_right + 2
+
+ @property
+ def parent(self):
+ """Returns this comment's parent. This is cached, hence its being a
+ property and not a method.
+ """
+ if not hasattr(self, '_parent'):
+ self._parent = Comment.query \
+ .filter(Comment.discussion_id == self.discussion_id) \
+ .filter(Comment.left < self.left) \
+ .filter(Comment.right > self.right) \
+ .order_by(Comment.left.desc()) \
+ .first()
+ return self._parent