def __init__(self, blog, author, title, summary, main='', dont_watch=0): qon.karma.HasKarma.__init__(self) Watchable.__init__(self) HasComments.__init__(self) self.blog = blog self.__deleted = 0 self.author = author self.title = title self.__summary = CompressedText(summary) if main: self.__main = CompressedText(main) else: self.__main = None self.__cached_html_summary = PersistentCache(self._update_html_cache) # history is a string, showing diffs as items are edited self.history = None self.date = datetime.utcnow() self.modified = None self.parent_blogitem = None # for comment, will point to parent blogitem upon add_comment(); otherwise None if dont_watch: """Comments aren't watchable.""" self.not_watchable = 1 else: # for watchable items only (not comments) self.__user_access = ConflictAvoidingOOBTree()
def upgradeToVersion1(self): # compress text self.__summary = CompressedText(self.summary) self.__main = CompressedText(self.main) del self.summary del self.main # create cache self.__cached_html_summary = PersistentCache(self._update_html_cache) self.version_upgrade_done()
def __init__(self, creator, title, description, end_date, choices): """Create a poll. Choices is a list of strings.""" Watchable.__init__(self) # PollContainer (set by container when added) self.container = None # User who created poll self.creator = creator # Date created/modified self.date = datetime.utcnow() # End date self.end_date = end_date # Title/Description self.title = title self.__description = CompressedText(description) self.__cached_html = PersistentCache(self._update_html_cache) # Other settings and vote data self.__data = PollData(choices) # cache of PollData items self.__num_votes_cast = None # poll index within container self.__item_index = None
class Poll(QonPersistent, Watchable): """A single Poll.""" persistenceVersion = 1 def __init__(self, creator, title, description, end_date, choices): """Create a poll. Choices is a list of strings.""" Watchable.__init__(self) # PollContainer (set by container when added) self.container = None # User who created poll self.creator = creator # Date created/modified self.date = datetime.utcnow() # End date self.end_date = end_date # Title/Description self.title = title self.__description = CompressedText(description) self.__cached_html = PersistentCache(self._update_html_cache) # Other settings and vote data self.__data = PollData(choices) # cache of PollData items self.__num_votes_cast = None # poll index within container self.__item_index = None def upgradeToVersion1(self): self.title = iso_8859_to_utf_8(self.title) def get_data(self): return self.__data def get_description(self): return self.__description.get_raw() def set_description(self, raw): self.__description.set_raw(raw) self.invalidate_html_cache() def has_voted(self, user): """True if user has already voted.""" return self.__data.has_voted(user) def user_vote(self, user): try: return self.__data.user_vote(user) except KeyError: return [] def user_vote_choices(self, user): """Return list of strings representing user's choices.""" choices = self.user_vote(user) pd = self.get_data() votes = [] for v in choices: votes.append(pd.choices[v]) return votes def user_votes_choices(self): """Returns dict of {user_id: [string1, string2, ...]} of user votes.""" vote_choice_data = self.__data.user_votes() vote_data = {} for user_id, choices in vote_choice_data.iteritems(): vote_data[user_id] = [self.__data.choices[i] for i in choices] return vote_data def is_active(self, as_of=None): """Return True if Poll is currently active.""" if not as_of: as_of = datetime.utcnow() is_active = as_of < self.end_date # check if we need to update our watchable_changed status if not is_active: if self.watchable_last_change() < self.end_date: self.watchable_changed() return is_active def cancel_poll(self, note=None): """Immediately ends poll, inserting note if any at beginning of description.""" self.end_date = datetime.utcnow() if note: self.set_description(note + '\n\n\n' + self.get_description()) # set changed when poll is canceled self.watchable_changed(self.end_date) def num_votes_cast(self): if not self.__num_votes_cast: self.__num_votes_cast = self.__data.num_votes_cast() return self.__num_votes_cast def valid_vote(self, user, choices): """Check various permissions to vote. If choices is not None, also checks validity of choices. Use can_vote to pre-check user's access before validity of choices. Returns: 1: if user is allowed to vote and choices includes required votes. 0: if choices are not valid -1: if user does not have access to vote -2: user has already voted -3: user has insufficient feedback score to vote -4: user has insufficnent bank to vote -5: user joined ned.com after poll started """ # general vote access if self.__data.vote_access == 'owner': if not self.container.ihb.is_owner(user): return -1 elif self.__data.vote_access == 'member': if not self.container.ihb.is_member(user) and not self.container.ihb.is_owner(user): return -1 # min karma if not self.enough_karma_to_vote(user): return -3 # karma bank cost if user.get_karma_bank_balance() < self.__data.karma_cost: return -4 # valid choices if choices and not self.__data.choices_valid(choices): return 0 # check if trying to vote for no choices if choices is not None and not choices: return 0 if not self.__data.voter_can_revise: if self.__data.has_voted(user): return -2 if not self.old_enough_to_vote(user): return -5 return 1 def can_pay_for_vote(self, user): if not user: return False return user.can_give_karma(self.get_data().karma_cost) def enough_karma_to_vote(self, user): return user.get_karma_score() >= self.get_data().min_karma def old_enough_to_vote(self, user): return user.get_user_data().member_since() <= self.date def can_vote(self, user): return self.valid_vote(user, choices=None) == 1 def can_see_intermediate_results(self, user): pd = self.get_data() vote_required = pd.vote_required_to_view has_voted = self.has_voted(user) if vote_required and not has_voted: return False if pd.intermediate_access == 'none': return False if pd.intermediate_access == 'all': return True if pd.intermediate_access == 'member': return self.container.ihb.is_member(user) or self.container.ihb.is_owner(user) if pd.intermediate_access == 'owner': return self.container.ihb.is_owner(user) return False def can_see_results(self, user): pd = self.get_data() if pd.results_access == 'all': return True if pd.results_access == 'member': return self.container.ihb.is_member(user) or self.container.ihb.is_owner(user) if pd.results_access == 'owner': return self.container.ihb.is_owner(user) return False def record_vote(self, user, choices): """Record a vote. Raises ValueError if valid_vote() check fails. Fails silently if poll is no longer open or if user can't pay. """ if not self.is_active(): return if self.valid_vote(user, choices) != 1: raise ValueError # see if new vote is identical to previous vote if self.has_voted(user): if choices == self.user_vote(user): return if not _ROLLOUT_TEST: # try to pay for item try: user.pay_karma(self.get_data().karma_cost) except NoKarmaToGive: return self.__data.record_vote(user, choices) self.__num_votes_cast = None # karma credit user.karma_activity_credit() def choice_list_to_choices(self, choice_list): """Convert a list of strings to their corresponding list of indices. Raises ValueError if there is an invalid item in choice_list. """ pd = self.get_data() choices = [] for item in choice_list: choices.append(pd.choices.index(item)) return choices def _set_item_index(self, index): """Used by PollContainer to set this item's index when added to container.""" self.__item_index = index def get_item_index(self): return self.__item_index def add_html_dependency(self, target): """Adds target as something self depends on for its HTML cache.""" self.__cached_html.add_dependency(target) def invalidate_html_cache(self): self.__cached_html.flush() def get_cached_html(self): return self.__cached_html.get().get_raw() def _update_html_cache(self): from qon.ui.blocks.wiki import rst_to_html return CompressedText(str(rst_to_html(self.get_description(), wiki=self.container.ihb.get_wiki(), container=self))) def disable_cache(self): self.__cached_html.disable_cache() def cache_disabled(self): return self.__cached_html.cache_disabled() def watchable_name(self): return self.title def watchable_changed(self, now=None): # tells container he has changed, too Watchable.watchable_changed(self, now) self.container.watchable_changed(now) def can_read(self, user): return self.container.ihb.can_read(user)
def set_main(self, raw): if not self.__main: self.__main = CompressedText() self.__main.set_raw(raw)
class BlogItem(QonPersistent, qon.karma.HasKarma, Watchable, HasComments): persistenceVersion = 6 def __init__(self, blog, author, title, summary, main='', dont_watch=0): qon.karma.HasKarma.__init__(self) Watchable.__init__(self) HasComments.__init__(self) self.blog = blog self.__deleted = 0 self.author = author self.title = title self.__summary = CompressedText(summary) if main: self.__main = CompressedText(main) else: self.__main = None self.__cached_html_summary = PersistentCache(self._update_html_cache) # history is a string, showing diffs as items are edited self.history = None self.date = datetime.utcnow() self.modified = None self.parent_blogitem = None # for comment, will point to parent blogitem upon add_comment(); otherwise None if dont_watch: """Comments aren't watchable.""" self.not_watchable = 1 else: # for watchable items only (not comments) self.__user_access = ConflictAvoidingOOBTree() def upgradeToVersion6(self): self.history = None def upgradeToVersion5(self): self.title = iso_8859_to_utf_8(self.title) def upgradeToVersion4(self): self.__deleted = self.deleted del self.deleted # upgrade HasComments HasComments._upgradeToVersion1(self) # elim __main's CompressedText if not self.get_main(): self.__main = None self.version_upgrade_done() def upgradeToVersion3(self): if not self.not_watchable: self.__user_access = ConflictAvoidingOOBTree() # get rid of old Readers attribute if hasattr(self, '_BlogItem__readers'): del self.__readers self.version_upgrade_done() def upgradeToVersion2(self): # do self.parent_blogitem 2005-03-17 self.parent_blogitem = None comments = HasComments.get_all_comments(self) for item in comments: item.parent_blogitem = self # point comments to point to itself self.version_upgrade_done() def upgradeToVersion1(self): # compress text self.__summary = CompressedText(self.summary) self.__main = CompressedText(self.main) del self.summary del self.main # create cache self.__cached_html_summary = PersistentCache(self._update_html_cache) self.version_upgrade_done() def is_deleted(self, use_attr=False): if use_attr or not self.parent_blogitem: return self.__deleted return self.parent_blogitem.get_comment_flags(self) def set_deleted(self, val): self.__deleted = bool(val) # cache/copy deleted attribute into paren't comment_flags # for fast lookup to avoid db reading each comment if self.parent_blogitem: self.parent_blogitem.set_comment_flags(self, bool(val)) # alex added so that recent items cache for parent blog gets marked as dirty self.watchable_changed() def set_deleted_note(self, note): self._deleted_note = note def get_deleted_note(self): return getattr(self, '_deleted_note', None) def can_read(self, user): return self.blog.ihb.can_read(user) def can_edit(self, user): return (self.author is user) or self.can_manage(user) def can_delete(self, user): """Return True if user can delete this item.""" # managers can always delete if self.can_manage(user): return True # authors can only delete if there are no undeleted comments if self.num_comments() == 0: return self.author is user return False def can_manage(self, user): """Return True if user can manage this item (usually a group owner).""" return self.blog.ihb and self.blog.ihb.can_manage(user) def can_show(self): """Return False if this item should be suppressed due to feedback score.""" if self.get_karma_score() < qon.karma.min_karma_to_show: return False if self.author.get_karma_score() < qon.karma.min_author_karma: return False return True def why_cant_show(self): """Only really useful when can_show()==False. Returns the reason for an item not being shown. Return value is ('item' | 'user', fbscore)""" if self.get_karma_score() < qon.karma.min_karma_to_show: return ('item', self.get_karma_score()) if self.author.get_karma_score() < qon.karma.min_author_karma: return ('user', self.author.get_karma_score()) return () def last_modified(self, consider_comments=True): if consider_comments: # this Watchable changes whenever a comment is added dt = self.watchable_last_change() if dt is never: dt = self.modified or self.date return dt else: return self.modified or self.date def new_comment(self, author, title, summary, main=''): """Create a new comment item and return it.""" comment = BlogItem(blog=self.blog, author=author, title=title, summary=summary, main=main, dont_watch=1) # Check to see if this new comment is a duplicate # of the previous comment. If so, ignore it, since # it's probably unintended, and just return None. if HasComments.is_duplicate(self, comment): comment = None else: self.add_comment(comment) # avoid 'bogus new to me' comment.read_item(author, datetime.utcnow()) author.karma_activity_credit() return comment def notify_karma_changed(self): """Called by HasKarma.add_karma.""" # also delegate to watchable_changed # self.watchable_changed() # removed by alex to keep blogitems from boldfacing when left feedback if self.blog and hasattr(self.blog, 'notify_karma_changed'): self.blog.notify_karma_changed() def add_comment(self, comment): comment.parent_blogitem = self HasComments.add_comment(self, comment) self.watchable_changed(comment.date) def watchable_name(self): return self.title def watchable_changed(self, now=None): # tells blog he has changed, too Watchable.watchable_changed(self, now) if self.blog: self.blog.watchable_changed(now) def watchable_modified_date(self): return self.last_modified() def can_get_karma_from(self, other): return other is not self.author def get_summary(self): return self.__summary.get_raw() def set_summary(self, raw): self._log_summary_change() self.__summary.set_raw(raw) self.invalidate_html_cache() def get_main(self): if not self.__main: return '' return self.__main.get_raw() def set_main(self, raw): if not self.__main: self.__main = CompressedText() self.__main.set_raw(raw) def _log_summary_change(self): import qon.log if hasattr(self.blog.ihb, 'get_user_id'): qon.log.edit_info('SetSummary\t%s\n%s' % (self.blog.ihb.get_user_id(), self.get_summary())) else: qon.log.edit_info('SetSummary2\t%s\n%s' % (self.blog.ihb.get_name(), self.get_summary())) # HTML cache methods def add_html_dependency(self, target): """Adds target as something self depends on for its HTML cache.""" self.__cached_html_summary.add_dependency(target) def invalidate_html_cache(self): self.__cached_html_summary.flush() def get_cached_html(self): return self.__cached_html_summary.get().get_raw() def _update_html_cache(self): from qon.ui.blocks.wiki import rst_to_html return CompressedText(str(rst_to_html(self.get_summary(), wiki=self.blog.ihb.get_wiki(), container=self))) def disable_cache(self): self.__cached_html_summary.disable_cache() def cache_disabled(self): return self.__cached_html_summary.cache_disabled() def read_item(self, user, now=None): """Notice that user has accessed this item. If we are a comment, we pass this on to our parent item, to catch up based on comment's submission date. """ if not hasattr(self, "_BlogItem__user_access"): # we don't keep track of user access -- we're a comment, # so just pass this on to our parent. if self.parent_blogitem: return self.parent_blogitem.read_item(user, self.date) return if not user: return now = now or datetime.utcnow() user_oid = unpack_oid(user._p_oid) dt, count = self.__user_access.get(user_oid, (never, 0)) now = max(now, dt) # increment hit count self.__user_access[user_oid] = (now, count + 1) def has_read_item(self, user, updated=None): if not hasattr(self, "_BlogItem__user_access"): return False if not user: return True dt, count = self._get_user_access(user) if count == 0: return False if updated: return dt >= updated else: return True def last_read(self, user): """Return datetime when user last read this item.""" dt, count = self._get_user_access(user) return dt def _get_user_access(self, user): if not hasattr(self, "_BlogItem__user_access"): return (never, 0) user_oid = unpack_oid(user._p_oid) return self.__user_access.get(user_oid, (never, 0)) def item_views(self): """Returns (number of views, number of readers).""" views = 0 readers = 0 for user_oid, (dt, count) in self.__user_access.iteritems(): if count > 0: # alex added if on 2006-10-09 readers += 1 views += count return (views, readers) def get_pageview_counts_per_user(self): """return a list of (user, counts)""" return_list = [] for user_oid, (dt, count) in self.__user_access.iteritems(): if count > 0: return_list.append((get_oid(pack_oid(user_oid)), count)) return return_list def get_title(self): # this is here and in WikiPage return self.title