def _new(cls, author, link, parent, body, ip): c = Comment(body = body, link_id = link._id, sr_id = link.sr_id, author_id = author._id, ip = ip) c._spam = author._spam #these props aren't relations if parent: c.parent_id = parent._id c._commit() link._incr('num_comments', 1) to = None if parent: to = Account._byID(parent.author_id) elif link.is_self: to = Account._byID(link.author_id) inbox_rel = None # only global admins can be message spammed. if to and (not c._spam or to.name in g.admins): inbox_rel = Inbox._add(to, c, 'inbox') return (c, inbox_rel)
def _new(cls, author, link, parent, body, ip): from r2.lib.db.queries import changed c = Comment(_ups=1, body=body, link_id=link._id, sr_id=link.sr_id, author_id=author._id, ip=ip) c._spam = author._spam # these props aren't relations if parent: c.parent_id = parent._id link._incr("num_comments", 1) to = None name = "inbox" if parent: to = Account._byID(parent.author_id, True) elif link.is_self and not link.noselfreply: to = Account._byID(link.author_id, True) name = "selfreply" c._commit() changed(link, True) # only the number of comments has changed inbox_rel = None # only global admins can be message spammed. if to and (not c._spam or to.name in g.admins): inbox_rel = Inbox._add(to, c, name) return (c, inbox_rel)
def _new(cls, author, link, parent, body, ip): c = Comment(_ups=1, body=body, link_id=link._id, sr_id=link.sr_id, author_id=author._id, ip=ip) c._spam = author._spam #these props aren't relations if parent: c.parent_id = parent._id link._incr('num_comments', 1) to = None name = 'inbox' if parent: to = Account._byID(parent.author_id) elif link.is_self: to = Account._byID(link.author_id) name = 'selfreply' c._commit() inbox_rel = None # only global admins can be message spammed. if to and (not c._spam or to.name in g.admins): inbox_rel = Inbox._add(to, c, name) return (c, inbox_rel)
def _new(cls, author, link, parent, body, ip,criticism=False): from r2.lib.db.queries import changed #We're turing it off for now... criticism = False c = Comment(_ups = 1, body = body, link_id = link._id, sr_id = link.sr_id, author_id = author._id, ip = ip) c._spam = author._spam c.criticism=criticism #these props aren't relations if parent: c.parent_id = parent._id #should increment based on crit flag #Each should contain the root author and its id, problem is the id isn't created yet if we're the root so have to be clever if criticism: link._incr("num_criticisms",1) if parent: c.rootauthor=parent.rootauthor if parent.rootid: c.rootid=parent.rootid else: c.rootid=parent._id else: c.rootauthor=author._id c.rootid=False else: link._incr('num_comments', 1) to = None name = 'inbox' if parent: to = Account._byID(parent.author_id, True) elif link.is_self and not link.noselfreply: to = Account._byID(link.author_id, True) name = 'selfreply' c._commit() changed(link, True) # link's number of comments changed inbox_rel = None # only global admins can be message spammed. # Don't send the message if the recipient has blocked # the author if to and ((not c._spam and author._id not in to.enemies) or to.name in g.admins): # When replying to your own comment, record the inbox # relation, but don't give yourself an orangered orangered = (to.name != author.name) inbox_rel = Inbox._add(to, c, name, orangered=orangered) return (c, inbox_rel)
def get_important_friends(cls, user_id, max_lookup=500, limit=100): a = Account._byID(user_id, data=True) # friends are returned chronologically by date, so pick the end of the list # for the most recent additions friends = Account._byID(a.friends[-max_lookup:], return_dict=False, data=True) # if we don't have a last visit for your friends, we don't care about them friends = [x for x in friends if hasattr(x, "last_visit")] # sort friends by most recent interactions friends.sort(key=lambda x: getattr(x, "last_visit"), reverse=True) return [x._id for x in friends[:limit]]
def get_important_friends(cls, user_id, max_lookup = 500, limit = 100): a = Account._byID(user_id, data = True) # friends are returned chronologically by date, so pick the end of the list # for the most recent additions friends = Account._byID(a.friends[-max_lookup:], return_dict = False, data = True) # if we don't have a last visit for your friends, we don't care about them friends = [x for x in friends if hasattr(x, "last_visit")] # sort friends by most recent interactions friends.sort(key = lambda x: getattr(x, "last_visit"), reverse = True) return [x._id for x in friends[:limit]]
def _new(cls, author, link, parent, body, ip, spam = False): c = Comment(body = body, link_id = link._id, sr_id = link.sr_id, author_id = author._id, ip = ip) c._spam = spam #these props aren't relations if parent: c.parent_id = parent._id c._commit() link._incr('num_comments', 1) inbox_rel = None if parent: to = Account._byID(parent.author_id) # only global admins can be message spammed. if not c._spam or to.name in g.admins: inbox_rel = Inbox._add(to, c, 'inbox') #clear that chache clear_memo('builder.link_comments2', link._id) return (c, inbox_rel)
def _new(cls, author, link, parent, body, ip, spam = False): c = Comment(body = body, link_id = link._id, sr_id = link.sr_id, author_id = author._id, ip = ip) c._spam = spam #these props aren't relations if parent: c.parent_id = parent._id c._commit() link._incr('num_comments', 1) if parent: to = Account._byID(parent.author_id) i = Inbox._add(to, c, 'inbox') #clear that chache clear_memo('builder.link_comments2', link._id) from admintools import admintools utils.worker.do(lambda: admintools.add_thing(c)) return c
def get_links(self, sort, time): from r2.lib.db import queries from r2.models import Link from r2.controllers.errors import UserRequiredException if not c.user_is_loggedin: raise UserRequiredException friends = self.get_important_friends(c.user._id) if not friends: return [] if g.use_query_cache: # with the precomputer enabled, this Subreddit only supports # being sorted by 'new'. it would be nice to have a # cleaner UI than just blatantly ignoring their sort, # though sort = 'new' time = 'all' friends = Account._byID(friends, return_dict=False) crs = [queries.get_submitted(friend, sort, time) for friend in friends] return queries.MergedCachedResults(crs) else: q = Link._query(Link.c.author_id == friends, sort = queries.db_sort(sort), data = True) if time != 'all': q._filter(queries.db_times[time]) return q
def accept(cls, r, correct=True): ''' sets the various reporting fields, but does nothing to the corresponding spam fields (handled by unreport)''' amount = 1 if correct else -1 oldamount = int(r._name) # do nothing if nothing has changed if amount == oldamount: return up_change, down_change = score_changes(amount, oldamount) # update the user who made the report r._thing1._incr('report_correct', up_change) r._thing1._incr('report_ignored', down_change) # update the amount cls.set_amount(r, amount) # update the thing's number of reports only if we made no # decision prior to this if oldamount == 0: # update the author and thing field if getattr(r._thing2, Report._field) > 0: r._thing2._incr(Report._field, -1) if hasattr(r._thing2, "author_id"): aid = r._thing2.author_id author = Account._byID(aid) if getattr(author, Report._field) > 0: author._incr(Report._field, -1) admintools.report(r._thing2, -1)
def add_props(cls, user, wrapped): Link.add_props(user, wrapped) try: if c.user_is_sponsor: promoted_by_ids = set(x.promoted_by for x in wrapped if hasattr(x,'promoted_by')) promoted_by_accounts = Account._byID(promoted_by_ids, data=True) else: promoted_by_accounts = {} except NotFound: # since this is just cosmetic, we can skip it altogether # if one isn't found or is broken promoted_by_accounts = {} for item in wrapped: # these are potentially paid for placement item.nofollow = True if item.promoted_by in promoted_by_accounts: item.promoted_by_name = promoted_by_accounts[item.promoted_by].name else: # keep the template from trying to read it item.promoted_by = None
def get_all_comments(self): from r2.lib.db import queries from r2.models import Comment from r2.controllers.errors import UserRequiredException if not c.user_is_loggedin: raise UserRequiredException friends = self.get_important_friends(c.user._id) if not friends: return [] if g.use_query_cache: # with the precomputer enabled, this Subreddit only supports # being sorted by 'new'. it would be nice to have a # cleaner UI than just blatantly ignoring their sort, # though sort = 'new' time = 'all' friends = Account._byID(friends, return_dict=False) crs = [ queries.get_comments(friend, sort, time) for friend in friends ] return queries.MergedCachedResults(crs) else: q = Comment._query(Comment.c.author_id == friends, sort=desc('_date'), data=True) return q
def add_props(cls, user, wrapped): #fetch parent links links = Link._byID(set(l.link_id for l in wrapped), True) #get srs for comments that don't have them (old comments) for cm in wrapped: if not hasattr(cm, 'sr_id'): cm.sr_id = links[cm.link_id].sr_id subreddits = Subreddit._byID(set(cm.sr_id for cm in wrapped), data=True,return_dict=False) can_reply_srs = set(s._id for s in subreddits if s.can_comment(user)) min_score = c.user.pref_min_comment_score cids = dict((w._id, w) for w in wrapped) for item in wrapped: item.link = links.get(item.link_id) if not hasattr(item, 'subreddit'): item.subreddit = item.subreddit_slow if hasattr(item, 'parent_id'): parent = Comment._byID(item.parent_id, data=True) parent_author = Account._byID(parent.author_id, data=True) item.parent_author = parent_author if not c.full_comment_listing and cids.has_key(item.parent_id): item.parent_permalink = '#' + utils.to36(item.parent_id) else: item.parent_permalink = parent.make_anchored_permalink(item.link, item.subreddit) else: item.parent_permalink = None item.parent_author = None item.can_reply = (item.sr_id in can_reply_srs) # Don't allow users to vote on their own comments item.votable = bool(c.user != item.author) # not deleted on profile pages, # deleted if spam and not author or admin item.deleted = (not c.profilepage and (item._deleted or (item._spam and item.author != c.user and not item.show_spam))) # don't collapse for admins, on profile pages, or if deleted item.collapsed = ((item.score < min_score) and not (c.profilepage or item.deleted or c.user_is_admin)) if not hasattr(item,'editted'): item.editted = False #will get updated in builder item.num_children = 0 item.score_fmt = Score.points item.permalink = item.make_permalink(item.link, item.subreddit)
def get_all_comments(self): from r2.lib.db import queries from r2.models import Comment from r2.controllers.errors import UserRequiredException if not c.user_is_loggedin: raise UserRequiredException friends = self.get_important_friends(c.user._id) if not friends: return [] if g.use_query_cache: # with the precomputer enabled, this Subreddit only supports # being sorted by 'new'. it would be nice to have a # cleaner UI than just blatantly ignoring their sort, # though sort = "new" time = "all" friends = Account._byID(friends, return_dict=False) crs = [queries.get_comments(friend, sort, time) for friend in friends] return queries.MergedCachedResults(crs) else: q = Comment._query(Comment.c.author_id == friends, sort=desc("_date"), data=True) return q
def add_props(cls, user, wrapped): #fetch parent links links = Link._byID(set(l.link_id for l in wrapped), True) #get srs for comments that don't have them (old comments) for cm in wrapped: if not hasattr(cm, 'sr_id'): cm.sr_id = links[cm.link_id].sr_id subreddits = Subreddit._byID(set(cm.sr_id for cm in wrapped), data=True, return_dict=False) can_reply_srs = set(s._id for s in subreddits if s.can_comment(user)) min_score = c.user.pref_min_comment_score cids = dict((w._id, w) for w in wrapped) for item in wrapped: item.link = links.get(item.link_id) if not hasattr(item, 'subreddit'): item.subreddit = item.subreddit_slow if hasattr(item, 'parent_id'): parent = Comment._byID(item.parent_id, data=True) parent_author = Account._byID(parent.author_id, data=True) item.parent_author = parent_author if not c.full_comment_listing and cids.has_key(item.parent_id): item.parent_permalink = '#' + utils.to36(item.parent_id) else: item.parent_permalink = parent.make_anchored_permalink( item.link, item.subreddit) else: item.parent_permalink = None item.parent_author = None item.can_reply = (item.sr_id in can_reply_srs) # Don't allow users to vote on their own comments item.votable = bool(c.user != item.author and not item.retracted) # not deleted on profile pages, # deleted if spam and not author or admin item.deleted = (not c.profilepage and (item._deleted or (item._spam and item.author != c.user and not item.show_spam))) # don't collapse for admins, on profile pages, or if deleted item.collapsed = ( (item.score < min_score) and not (c.profilepage or item.deleted or c.user_is_admin)) if not hasattr(item, 'editted'): item.editted = False #will get updated in builder item.num_children = 0 item.score_fmt = Score.points item.permalink = item.make_permalink(item.link, item.subreddit) item.can_be_deleted = item.can_delete()
def accept(cls, r, correct = True): ''' sets the various reporting fields, but does nothing to the corresponding spam fields (handled by unreport)''' amount = 1 if correct else -1 oldamount = int(r._name) # do nothing if nothing has changed if amount == oldamount: return up_change, down_change = score_changes(amount, oldamount) # update the user who made the report r._thing1._incr('report_correct', up_change) r._thing1._incr('report_ignored', down_change) # update the amount cls.set_amount(r, amount) # update the thing's number of reports only if we made no # decision prior to this if oldamount == 0: # update the author and thing field if getattr(r._thing2, Report._field) > 0: r._thing2._incr(Report._field, -1) aid = r._thing2.author_id author = Account._byID(aid) if getattr(author, Report._field) > 0: author._incr(Report._field, -1) admintools.report(r._thing2, -1)
def add_props(cls, user, wrapped): Link.add_props(user, wrapped) try: if c.user_is_sponsor: promoted_by_ids = set(x.promoted_by for x in wrapped if hasattr(x, 'promoted_by')) promoted_by_accounts = Account._byID(promoted_by_ids, data=True) else: promoted_by_accounts = {} except NotFound: # since this is just cosmetic, we can skip it altogether # if one isn't found or is broken promoted_by_accounts = {} for item in wrapped: # these are potentially paid for placement item.nofollow = True if item.promoted_by in promoted_by_accounts: item.promoted_by_name = promoted_by_accounts[ item.promoted_by].name else: # keep the template from trying to read it item.promoted_by = None
def make_permalink_title(self, link): author = Account._byID(self.author_id, data=True).name params = { 'author': _force_unicode(author), 'title': _force_unicode(link.title), 'site': c.site.title } return strings.permalink_title % params
def _send_post_notifications(self, link, comment, parent): if parent: to = Account._byID(parent.author_id) else: if not link.notify_on_comment: return None elif comment.author_id != link.author_id: # Send notification if the comment wasn't by the link author to = Account._byID(link.author_id) else: return None # only global admins can be message spammed. if self._spam and to.name not in g.admins: return None return Inbox._add(to, self, 'inbox')
def _new(cls, author, link, parent, body, ip): from r2.lib.db.queries import changed c = Comment(_ups=1, body=body, link_id=link._id, sr_id=link.sr_id, author_id=author._id, ip=ip) c._spam = author._spam if author._spam: g.stats.simple_event('spam.autoremove.comment') #these props aren't relations if parent: c.parent_id = parent._id link._incr('num_comments', 1) to = None name = 'inbox' if parent: to = Account._byID(parent.author_id, True) elif link.is_self and not link.noselfreply: to = Account._byID(link.author_id, True) name = 'selfreply' c._commit() changed(link, True) # link's number of comments changed inbox_rel = None # only global admins can be message spammed. # Don't send the message if the recipient has blocked # the author if to and ((not c._spam and author._id not in to.enemies) or to.name in g.admins): # When replying to your own comment, record the inbox # relation, but don't give yourself an orangered orangered = (to.name != author.name) inbox_rel = Inbox._add(to, c, name, orangered=orangered) return (c, inbox_rel)
def get_reported_authors(cls, time = None, sort = None): reports = {} for t_cls in (Link, Comment, Message): q = t_cls._query(t_cls.c._spam == False, t_cls.c.reported > 0, data = True) q._sort = desc("_date") if time: q._filter(time) reports.update(Report.reported(things = list(q), amount = 0)) # at this point, we have a full list of reports made on the interval specified # build up an author to report list authors = Account._byID([k[1].author_id for k, v in reports.iteritems()], data = True) if reports else [] # and build up a report on each author author_rep = {} for (tattler, thing, amount), r in reports.iteritems(): aid = thing.author_id if not author_rep.get(aid): author_rep[aid] = Storage(author = authors[aid]) author_rep[aid].num_reports = 1 author_rep[aid].acct_correct = tattler.report_correct author_rep[aid].acct_wrong = tattler.report_ignored author_rep[aid].most_recent = r._date author_rep[aid].reporters = set([tattler]) else: author_rep[aid].num_reports += 1 author_rep[aid].acct_correct += tattler.report_correct author_rep[aid].acct_wrong += tattler.report_ignored if author_rep[aid].most_recent < r._date: author_rep[aid].most_recent = r._date author_rep[aid].reporters.add(tattler) authors = author_rep.values() if sort == "hot": def report_hotness(a): return a.acct_correct / max(a.acct_wrong + a.acct_correct,1) def better_reporter(a, b): q = report_hotness(b) - report_hotness(a) if q == 0: return b.acct_correct - a.acct_correct else: return 1 if q > 0 else -1 authors.sort(better_reporter) if sort == "top": authors.sort(lambda x, y: y.num_reports - x.num_reports) elif sort == "new": def newer_reporter(a, b): t = b.most_recent - a.most_recent t0 = datetime.timedelta(0) return 1 if t > t0 else -1 if t < t0 else 0 authors.sort(newer_reporter) return authors
def vote(cls, sub, obj, dir, ip, spam=False, organic=False): from admintools import valid_user, valid_thing, update_score from r2.lib.count import incr_counts sr = obj.subreddit_slow kind = obj.__class__.__name__.lower() karma = sub.karma(kind, sr) is_self_link = kind == "link" and hasattr(obj, "is_self") and obj.is_self # check for old vote rel = cls.rel(sub, obj) oldvote = list(rel._query(rel.c._thing1_id == sub._id, rel.c._thing2_id == obj._id, data=True)) amount = 1 if dir is True else 0 if dir is None else -1 is_new = False # old vote if len(oldvote): v = oldvote[0] oldamount = int(v._name) v._name = str(amount) # these still need to be recalculated old_valid_thing = v.valid_thing v.valid_thing = valid_thing(v, karma) and v.valid_thing and not spam v.valid_user = v.valid_user and v.valid_thing and valid_user(v, sr, karma) # new vote else: is_new = True oldamount = 0 v = rel(sub, obj, str(amount)) v.author_id = obj.author_id v.ip = ip old_valid_thing = v.valid_thing = valid_thing(v, karma) and not spam v.valid_user = v.valid_thing and valid_user(v, sr, karma) and not is_self_link if organic: v.organic = organic v._commit() up_change, down_change = score_changes(amount, oldamount) update_score(obj, up_change, down_change, v.valid_thing, old_valid_thing) if v.valid_user: author = Account._byID(obj.author_id, data=True) author.incr_karma(kind, sr, up_change - down_change) # update the sr's valid vote count if is_new and v.valid_thing and kind == "link": if sub._id != obj.author_id: incr_counts([sr]) return v
def _new(cls, author, link, parent, body, ip): from r2.lib.db.queries import changed c = Comment(_ups=1, body=body, link_id=link._id, sr_id=link.sr_id, author_id=author._id, ip=ip) c._spam = author._spam #these props aren't relations if parent: c.parent_id = parent._id link._incr('num_comments', 1) to = None name = 'inbox' if parent: to = Account._byID(parent.author_id, True) elif link.is_self and not link.noselfreply: to = Account._byID(link.author_id, True) name = 'selfreply' c._commit() changed(link, True) # link's number of comments changed inbox_rel = None # only global admins can be message spammed. # Don't send the message if the recipient has blocked # the author if to and ((not c._spam and author._id not in to.enemies) or to.name in g.admins): # When replying to your own comment, record the inbox # relation, but don't give yourself an orangered orangered = (to.name != author.name) inbox_rel = Inbox._add(to, c, name, orangered=orangered) return (c, inbox_rel)
def export_row(self, aliases): userid = self._thing1_id pollid = self._thing2_id if hasattr(self, 'anonymous') and self.anonymous: if not userid in aliases: aliases[userid] = aliases['next_alias'] aliases['next_alias'] = aliases['next_alias'] + 1 username = aliases[userid] else: username = Account._byID(userid, data = True).name return "\"{0}\",\"{1}\",\"{2}\",\"{3}\"".format(username, pollid, self.response, self.date)
def get_reporters(cls, time=None, sort=None): query = cls._query(cls.c._name == '0', eager_load=False, data=False, thing_data=False) if time: query._filter(time) query._sort = desc("_date") account_dict = {} min_report_time = {} for r in query: account_dict[r._thing1_id] = account_dict.get(r._thing1_id, 0) + 1 if min_report_time.get(r._thing1_id): min_report_time[r._thing1_id] = min( min_report_time[r._thing1_id], r._date) else: min_report_time[r._thing1_id] = r._date # grab users in chunks of 50 c_size = 50 accounts = account_dict.keys() accounts = [ Account._byID(accounts[i:i + c_size], return_dict=False, data=True) for i in xrange(0, len(accounts), c_size) ] accts = [] for a in accounts: accts.extend(a) if sort == "hot" or sort == "top": def report_hotness(a): return a.report_correct / max( a.report_ignored + a.report_correct, 1) def better_reporter(a, b): q = report_hotness(b) - report_hotness(a) if q == 0: return b.report_correct - a.report_correct else: return 1 if q > 0 else -1 accts.sort(better_reporter) elif sort == "new": def newer_reporter(a, b): t = (min_report_time[b._id] - min_report_time[a._id]) t0 = datetime.timedelta(0) return 1 if t > t0 else -1 if t < t0 else 0 accts.sort(newer_reporter) return accts
def export_row(self, aliases): userid = self._thing1_id pollid = self._thing2_id if hasattr(self, 'anonymous') and self.anonymous: if not userid in aliases: aliases[userid] = aliases['next_alias'] aliases['next_alias'] = aliases['next_alias'] + 1 username = aliases[userid] else: username = Account._byID(userid, data=True).name return "\"{0}\",\"{1}\",\"{2}\",\"{3}\"".format( username, pollid, self.response, self.date)
def _add(cls, sr, obj, *a, **kw): i = ModeratorInbox(sr, obj, *a, **kw) i.new = True i._commit() if not sr._loaded: sr._load() moderators = Account._byID(sr.moderator_ids(), data=True, return_dict=False) for m in moderators: if obj.author_id != m._id and not getattr(m, "modmsgtime", None): m.modmsgtime = obj._date m._commit() return i
def _add(cls, sr, obj, *a, **kw): i = ModeratorInbox(sr, obj, *a, **kw) i.new = True i._commit() if not sr._loaded: sr._load() moderators = Account._byID(sr.moderator_ids(), return_dict=False) for m in moderators: if obj.author_id != m._id and not getattr(m, 'modmsgtime', None): m.modmsgtime = obj._date m._commit() return i
def _new(cls, author, link, parent, body, ip): from r2.lib.db.queries import changed c = Comment(_ups = 1, body = body, link_id = link._id, sr_id = link.sr_id, author_id = author._id, ip = ip) c._spam = author._spam #these props aren't relations if parent: c.parent_id = parent._id link._incr('num_comments', 1) to = None name = 'inbox' if parent: to = Account._byID(parent.author_id, True) elif link.is_self and not link.noselfreply: to = Account._byID(link.author_id, True) name = 'selfreply' c._commit() changed(link, True) # only the number of comments has changed inbox_rel = None # only global admins can be message spammed. if to and (not c._spam or to.name in g.admins): inbox_rel = Inbox._add(to, c, name) return (c, inbox_rel)
def from_queue(self, max_date, batch_limit=50, kind=None): from r2.models import is_banned_IP, Account, Thing keep_trying = True min_id = None s = self.queue_table while keep_trying: where = [s.c.date < max_date] if min_id: where.append(s.c.uid > min_id) if kind: where.append(s.c.kind == kind) res = sa.select([ s.c.to_addr, s.c.account_id, s.c.from_name, s.c.fullname, s.c.body, s.c.kind, s.c.ip, s.c.date, s.c.uid, s.c.msg_hash, s.c.fr_addr, s.c.reply_to ], sa.and_(*where), order_by=s.c.uid, limit=batch_limit).execute() res = res.fetchall() if not res: break # batch load user accounts aids = [x[1] for x in res if x[1] > 0] accts = Account._byID(aids, data=True, return_dict=True) if aids else {} # batch load things tids = [x[3] for x in res if x[3]] things = Thing._by_fullname(tids, data=True, return_dict=True) if tids else {} # make sure no IPs have been banned in the mean time ips = set(x[6] for x in res) ips = dict((ip, is_banned_IP(ip)) for ip in ips) # get the lower bound date for next iteration min_id = max(x[8] for x in res) # did we not fetch them all? keep_trying = (len(res) == batch_limit) for (addr, acct, fname, fulln, body, kind, ip, date, uid, msg_hash, fr_addr, reply_to) in res: yield (accts.get(acct), things.get(fulln), addr, fname, date, ip, ips[ip], kind, msg_hash, body, fr_addr, reply_to)
def add_props(cls, user, wrapped): #TODO global-ish functions that shouldn't be here? #reset msgtime after this request msgtime = c.have_messages #load the "to" field if required to_ids = set(w.to_id for w in wrapped) tos = Account._byID(to_ids, True) if to_ids else {} for item in wrapped: item.to = tos[item.to_id] if msgtime and item._date >= msgtime: item.new = True else: item.new = False item.score_fmt = Score.none
def from_queue(self, max_date, batch_limit = 50, kind = None): from r2.models import is_banned_IP, Account, Thing keep_trying = True min_id = None s = self.queue_table while keep_trying: where = [s.c.date < max_date] if min_id: where.append(s.c.uid > min_id) if kind: where.append(s.c.kind == kind) res = sa.select([s.c.to_addr, s.c.account_id, s.c.from_name, s.c.fullname, s.c.body, s.c.kind, s.c.ip, s.c.date, s.c.uid, s.c.msg_hash, s.c.fr_addr, s.c.reply_to], sa.and_(*where), order_by = s.c.uid, limit = batch_limit).execute() res = res.fetchall() if not res: break # batch load user accounts aids = [x[1] for x in res if x[1] > 0] accts = Account._byID(aids, data = True, return_dict = True) if aids else {} # batch load things tids = [x[3] for x in res if x[3]] things = Thing._by_fullname(tids, data = True, return_dict = True) if tids else {} # make sure no IPs have been banned in the mean time ips = set(x[6] for x in res) ips = dict((ip, is_banned_IP(ip)) for ip in ips) # get the lower bound date for next iteration min_id = max(x[8] for x in res) # did we not fetch them all? keep_trying = (len(res) == batch_limit) for (addr, acct, fname, fulln, body, kind, ip, date, uid, msg_hash, fr_addr, reply_to) in res: yield (accts.get(acct), things.get(fulln), addr, fname, date, ip, ips[ip], kind, msg_hash, body, fr_addr, reply_to)
def unreport(things, correct=False, auto = False, banned_by = ''): things = tup(things) # load authors (to set the spammer flag) try: aids = set(t.author_id for t in things) except AttributeError: aids = None authors = Account._byID(tuple(aids), data=True) if aids else {} # load all reports (to set their amount to be +/-1) reports = Report.reported(things=things, amount = 0) # mark the reports as finalized: for r in reports.values(): Report.accept(r, correct) amount = 1 if correct else -1 spammer = {} for t in things: # clean up inconsistencies if getattr(t, Report._field) != 0: setattr(t, Report._field, 0) t._commit() # flag search indexer that something has changed tc.changed(t) # update the spam flag if t._spam != correct and hasattr(t, 'author_id'): # tally the spamminess of the author spammer[t.author_id] = spammer.get(t.author_id,0) + amount #author = authors.get(t.author_id) #if author: # karma_whack(author, t.__class__, -amount) #will be empty if the items didn't have authors for s, v in spammer.iteritems(): if authors[s].spammer + v >= 0: authors[s]._incr('spammer', v) # mark all as spam admintools.spam(things, amount = amount, auto = auto, banned_by = banned_by)
def unreport(things, correct=False, auto=False, banned_by=''): things = tup(things) # load authors (to set the spammer flag) try: aids = set(t.author_id for t in things) except AttributeError: aids = None authors = Account._byID(tuple(aids), data=True) if aids else {} # load all reports (to set their amount to be +/-1) reports = Report.reported(things=things, amount=0) # mark the reports as finalized: for r in reports.values(): Report.accept(r, correct) amount = 1 if correct else -1 spammer = {} for t in things: # clean up inconsistencies if getattr(t, Report._field) != 0: setattr(t, Report._field, 0) t._commit() # flag search indexer that something has changed tc.changed(t) # update the spam flag if t._spam != correct and hasattr(t, 'author_id'): # tally the spamminess of the author spammer[t.author_id] = spammer.get(t.author_id, 0) + amount #author = authors.get(t.author_id) #if author: # karma_whack(author, t.__class__, -amount) #will be empty if the items didn't have authors for s, v in spammer.iteritems(): if authors[s].spammer + v >= 0: authors[s]._incr('spammer', v) # mark all as spam admintools.spam(things, amount=amount, auto=auto, banned_by=banned_by)
def add_props(cls, user, wrapped): #TODO global-ish functions that shouldn't be here? #reset msgtime after this request msgtime = c.have_messages #load the "to" field if required to_ids = set(w.to_id for w in wrapped) tos = Account._byID(to_ids, True) if to_ids else {} links = Link._byID(set(l.link_id for l in wrapped if l.was_comment), data = True, return_dict = True) subreddits = Subreddit._byID(set(l.sr_id for l in links.values()), data = True, return_dict = True) parents = Comment._byID(set(l.parent_id for l in wrapped if hasattr(l, "parent_id") and l.was_comment), data = True, return_dict = True) for item in wrapped: item.to = tos[item.to_id] if msgtime and item._date >= msgtime: item.new = True else: item.new = False item.score_fmt = Score.none item.message_style = "" if item.was_comment: link = links[item.link_id] sr = subreddits[link.sr_id] item.link_title = link.title item.link_permalink = link.make_permalink(sr) if hasattr(item, "parent_id"): item.subject = _('comment reply') item.message_style = "comment-reply" parent = parents[item.parent_id] item.parent = parent._fullname item.parent_permalink = parent.make_permalink(link, sr) else: item.subject = _('post reply') item.message_style = "post-reply" # Run this last Printable.add_props(user, wrapped)
def get_reporters(cls, time = None, sort = None): query = cls._query(cls.c._name == '0', eager_load = False, data = False, thing_data = False) if time: query._filter(time) query._sort = desc("_date") account_dict = {} min_report_time = {} for r in query: account_dict[r._thing1_id] = account_dict.get(r._thing1_id, 0) + 1 if min_report_time.get(r._thing1_id): min_report_time[r._thing1_id] = min(min_report_time[r._thing1_id], r._date) else: min_report_time[r._thing1_id] = r._date # grab users in chunks of 50 c_size = 50 accounts = account_dict.keys() accounts = [Account._byID(accounts[i:i+c_size], return_dict = False, data = True) for i in xrange(0, len(accounts), c_size)] accts = [] for a in accounts: accts.extend(a) if sort == "hot" or sort == "top": def report_hotness(a): return a.report_correct / max(a.report_ignored + a.report_correct,1) def better_reporter(a, b): q = report_hotness(b) - report_hotness(a) if q == 0: return b.report_correct - a.report_correct else: return 1 if q > 0 else -1 accts.sort(better_reporter) elif sort == "new": def newer_reporter(a, b): t = (min_report_time[b._id] - min_report_time[a._id]) t0 = datetime.timedelta(0) return 1 if t > t0 else -1 if t < t0 else 0 accts.sort(newer_reporter) return accts
def _new(cls, author, link, parent, body, ip, spam=False, date=None): comment = Comment(body=body, link_id=link._id, sr_id=link.sr_id, author_id=author._id, ip=ip, date=date) comment._spam = spam #these props aren't relations if parent: comment.parent_id = parent._id comment._commit() link._incr('num_comments', 1) inbox_rel = None if parent: to = Account._byID(parent.author_id) # only global admins can be message spammed. if not comment._spam or to.name in g.admins: inbox_rel = Inbox._add(to, comment, 'inbox') #clear that chache clear_memo('builder.link_comments2', link._id) # flag search indexer that something has changed tc.changed(comment) #update last modified set_last_modified(author, 'overview') set_last_modified(author, 'commented') set_last_modified(link, 'comments') #update the comment cache from r2.lib.comment_tree import add_comment add_comment(comment) return (comment, inbox_rel)
def _new(cls, author, link, parent, body, ip, spam = False, date = None): comment = Comment(body = body, link_id = link._id, sr_id = link.sr_id, author_id = author._id, ip = ip, date = date) comment._spam = spam #these props aren't relations if parent: comment.parent_id = parent._id comment._commit() link._incr('num_comments', 1) inbox_rel = None if parent: to = Account._byID(parent.author_id) # only global admins can be message spammed. if not comment._spam or to.name in g.admins: inbox_rel = Inbox._add(to, comment, 'inbox') #clear that chache clear_memo('builder.link_comments2', link._id) # flag search indexer that something has changed tc.changed(comment) #update last modified set_last_modified(author, 'overview') set_last_modified(author, 'commented') set_last_modified(link, 'comments') #update the comment cache from r2.lib.comment_tree import add_comment add_comment(comment) return (comment, inbox_rel)
def get_all_comments(self): from r2.lib.db import queries if not c.user_is_loggedin: raise UserRequiredException friends = self.get_important_friends(c.user._id) if not friends: return [] # with the precomputer enabled, this Subreddit only supports # being sorted by 'new'. it would be nice to have a # cleaner UI than just blatantly ignoring their sort, # though sort = "new" time = "all" friends = Account._byID(friends, return_dict=False) crs = [queries.get_comments(friend, sort, time) for friend in friends] return queries.MergedCachedResults(crs)
def get_all_comments(self): from r2.lib.db import queries if not c.user_is_loggedin: raise UserRequiredException friends = self.get_important_friends(c.user._id) if not friends: return [] # with the precomputer enabled, this Subreddit only supports # being sorted by 'new'. it would be nice to have a # cleaner UI than just blatantly ignoring their sort, # though sort = 'new' time = 'all' friends = Account._byID(friends, return_dict=False) crs = [queries.get_comments(friend, sort, time) for friend in friends] return queries.MergedCachedResults(crs)
def new(cls, user, thing): # check if this report exists already! rel = cls.rel(user, thing) oldreport = list( rel._query(rel.c._thing1_id == user._id, rel.c._thing2_id == thing._id, data=True)) # stop if we've seen this before, so that we never get the # same report from the same user twice if oldreport: return oldreport[0] r = Report(user, thing, '0', amount=0) if not thing._loaded: thing._load() # mark item as reported thing._incr(cls._field) # mark author as reported if hasattr(thing, 'author_id'): aid = thing.author_id author = Account._byID(aid) author._incr(cls._field) # mark user as having made a report user._incr('report_made') r._commit() admintools.report(thing) # if the thing is already marked as spam, accept the report if thing._spam: cls.accept(r) else: # set the report amount to 0, updating the cache in the process cls.set_amount(r, 0) return r
def new(cls, user, thing): # check if this report exists already! rel = cls.rel(user, thing) oldreport = list(rel._query(rel.c._thing1_id == user._id, rel.c._thing2_id == thing._id, data = True)) # stop if we've seen this before, so that we never get the # same report from the same user twice if oldreport: return oldreport[0] r = Report(user, thing, '0', amount = 0) if not thing._loaded: thing._load() # mark item as reported thing._incr(cls._field) # mark author as reported if hasattr(thing, 'author_id'): aid = thing.author_id author = Account._byID(aid) author._incr(cls._field) # mark user as having made a report user._incr('report_made') r._commit() admintools.report(thing) # if the thing is already marked as spam, accept the report if thing._spam: cls.accept(r) else: # set the report amount to 0, updating the cache in the process cls.set_amount(r, 0) return r
def _send_post_notifications(self, link, comment, parent): dashto = [] for subscriber in Subscription._query( Subscription.c._thing2_id == (link._id), Subscription.c._name == 'subscription'): if not subscriber._thing1_id == comment.author_id: dashto.append(Account._byID(subscriber._thing1_id)) if link.notify_on_comment and not link.author_id == comment.author_id: dashto.append(Account._byID(link.author_id)) for user in dashto: s = SubscriptionStorage(user, comment, name='subscriptionstorage') s._commit() to = [] if parent: if not parent.author_id == comment.author_id: to.append(Account._byID(parent.author_id)) for subscriber in CommentSubscription._query( CommentSubscription.c._thing2_id == (parent._id), CommentSubscription.c._name == 'commentsubscription'): if not subscriber._thing1_id == comment.author_id: to.append(Account._byID(subscriber._thing1_id)) else: for subscriber in Subscription._query( Subscription.c._thing2_id == (link._id), Subscription.c._name == 'subscription'): if not subscriber._thing1_id == comment.author_id: to.append(Account._byID(subscriber._thing1_id)) if link.notify_on_comment and not link.author_id == comment.author_id: to.append(Account._byID(link.author_id)) if len(to) == 0: return None # only global admins can be message spammed. if self._spam and to.name not in g.admins: return None for user in to: Inbox._add(user, self, 'inbox') return True
def _send_post_notifications(self, link, comment, parent): dashto = [] for subscriber in Subscription._query(Subscription.c._thing2_id == (link._id), Subscription.c._name == 'subscription'): if not subscriber._thing1_id == comment.author_id: dashto.append(Account._byID(subscriber._thing1_id)) if link.notify_on_comment and not link.author_id == comment.author_id: dashto.append(Account._byID(link.author_id)) for user in dashto: s = SubscriptionStorage(user, comment, name='subscriptionstorage') s._commit() to = [] if parent: if not parent.author_id == comment.author_id: to.append(Account._byID(parent.author_id)) for subscriber in CommentSubscription._query(CommentSubscription.c._thing2_id == (parent._id), CommentSubscription.c._name == 'commentsubscription'): if not subscriber._thing1_id == comment.author_id: to.append(Account._byID(subscriber._thing1_id)) else: for subscriber in Subscription._query(Subscription.c._thing2_id == (link._id), Subscription.c._name == 'subscription'): if not subscriber._thing1_id == comment.author_id: to.append(Account._byID(subscriber._thing1_id)) if link.notify_on_comment and not link.author_id == comment.author_id: to.append(Account._byID(link.author_id)) if len(to) == 0: return None # only global admins can be message spammed. if self._spam and to.name not in g.admins: return None for user in to: Inbox._add(user, self, 'inbox') return True
def _new(cls, author, to, subject, body, ip, parent=None, sr=None): m = Message(subject=subject, body=body, author_id=author._id, new=True, ip=ip) m._spam = author._spam sr_id = None # check to see if the recipient is a subreddit and swap args accordingly if to and isinstance(to, Subreddit): to_subreddit = True to, sr = None, to else: to_subreddit = False if sr: sr_id = sr._id if parent: m.parent_id = parent._id if parent.first_message: m.first_message = parent.first_message else: m.first_message = parent._id if parent.sr_id: sr_id = parent.sr_id if not to and not sr_id: raise CreationError, "Message created with neither to nor sr_id" m.to_id = to._id if to else None if sr_id is not None: m.sr_id = sr_id m._commit() inbox_rel = None if sr_id and not sr: sr = Subreddit._byID(sr_id) inbox_rel = [] if sr_id: # if there is a subreddit id, and it's either a reply or # an initial message to an SR, add to the moderator inbox # (i.e., don't do it for automated messages from the SR) if parent or to_subreddit: inbox_rel.append(ModeratorInbox._add(sr, m, 'inbox')) if author.name in g.admins: m.distinguished = 'admin' m._commit() elif sr.is_moderator(author): m.distinguished = 'yes' m._commit() # if there is a "to" we may have to create an inbox relation as well # also, only global admins can be message spammed. if to and (not m._spam or to.name in g.admins): # if the current "to" is not a sr moderator, # they need to be notified if not sr_id or not sr.is_moderator(to): # Record the inbox relation, but don't give the user # an orangered, if they PM themselves. # Don't notify on PMs from blocked users, either orangered = (to.name != author.name and author._id not in to.enemies) inbox_rel.append( Inbox._add(to, m, 'inbox', orangered=orangered)) # find the message originator elif sr_id and m.first_message: first = Message._byID(m.first_message, True) orig = Account._byID(first.author_id, True) # if the originator is not a moderator... if not sr.is_moderator(orig) and orig._id != author._id: inbox_rel.append(Inbox._add(orig, m, 'inbox')) return (m, inbox_rel)
def cast_vote(sub, obj, dir, ip, vote_info, cheater, timer, date): from r2.models.admintools import valid_user, valid_thing, update_score from r2.lib.count import incr_sr_count from r2.lib.db import queries names_by_dir = {True: "1", None: "0", False: "-1"} # `vote` mimics the old pg vote rel interface so downstream code doesn't # need to change. (but it totally needn't stay that way forever!) vote = Storage( _thing1=sub, _thing2=obj, _name=names_by_dir[dir], _date=date, valid_thing=True, valid_user=True, ip=ip, ) # these track how much ups/downs should change on `obj` ups_delta = 1 if int(vote._name) > 0 else 0 downs_delta = 1 if int(vote._name) < 0 else 0 # see if the user has voted on this thing before pgrel = Vote.rel(sub, obj) pgoldvote = pgrel._fast_query(sub, obj, ["-1", "0", "1"]).values() try: pgoldvote = filter(None, pgoldvote)[0] except IndexError: pgoldvote = None timer.intermediate("pg_read_vote") if pgoldvote: # old_vote is mimicking `{Link,Comment}VoteDetailsByThing` here because # that will eventually be exactly what it is old_vote = { "direction": pgoldvote._name, "valid_thing": pgoldvote.valid_thing, "valid_user": pgoldvote.valid_user, "ip": getattr(pgoldvote, "ip", None), } vote.valid_thing = old_vote["valid_thing"] vote.valid_user = old_vote["valid_user"] if vote._name == old_vote["direction"]: # the old vote and new vote are the same. bail out. return vote # remove the old vote from the score old_direction = int(old_vote["direction"]) ups_delta -= 1 if old_direction > 0 else 0 downs_delta -= 1 if old_direction < 0 else 0 else: old_vote = None # calculate valid_thing and valid_user sr = obj.subreddit_slow kind = obj.__class__.__name__.lower() karma = sub.karma(kind, sr) if vote.valid_thing: vote.valid_thing = valid_thing(vote, karma, cheater, vote_info) if vote.valid_user: vote.valid_user = vote.valid_thing and valid_user(vote, sr, karma) if kind == "link" and getattr(obj, "is_self", False): # self-posts do not generate karma vote.valid_user = False g.stats.simple_event("vote.valid_thing." + str(vote.valid_thing).lower()) g.stats.simple_event("vote.valid_user." + str(vote.valid_user).lower()) # write out the new/modified vote to postgres if pgoldvote: pgvote = pgoldvote pgvote._name = vote._name else: pgvote = pgrel(sub, obj, vote._name, date=vote._date, ip=ip) pgvote.valid_thing = vote.valid_thing pgvote.valid_user = vote.valid_user pgvote._commit() timer.intermediate("pg_write_vote") # update various score/karma/vote counts if not (not old_vote and obj.author_id == sub._id and vote._name == "1"): # newly created objects start out with _ups = 1, so we skip updating # their score here if this is the author's own initial vote on it. old_valid_thing = old_vote["valid_thing"] if old_vote else True update_score(obj, ups_delta, downs_delta, vote, old_valid_thing) timer.intermediate("pg_update_score") if vote.valid_user: author = Account._byID(obj.author_id, data=True) author.incr_karma(kind, sr, ups_delta - downs_delta) timer.intermediate("pg_incr_karma") if not old_vote and vote.valid_thing and kind == "link": if sub._id != obj.author_id: incr_sr_count(sr) timer.intermediate("incr_sr_counts") # write the vote to cassandra VotesByAccount.copy_from(vote, vote_info) timer.intermediate("cassavotes") # update the search index queries.changed(vote._thing2, boost_only=True) timer.intermediate("changed") return vote
def make_permalink_title(self, link): author = Account._byID(self.author_id, data=True).name params = {'author' : _force_unicode(author), 'title' : _force_unicode(link.title), 'site' : c.site.title} return strings.permalink_title % params
def vote(cls, sub, obj, dir, ip, organic=False, cheater=False): from admintools import valid_user, valid_thing, update_score from r2.lib.count import incr_sr_count from r2.lib.db import queries sr = obj.subreddit_slow kind = obj.__class__.__name__.lower() karma = sub.karma(kind, sr) is_self_link = (kind == 'link' and getattr(obj, 'is_self', False)) #check for old vote rel = cls.rel(sub, obj) oldvote = rel._fast_query(sub, obj, ['-1', '0', '1']).values() oldvote = filter(None, oldvote) amount = 1 if dir is True else 0 if dir is None else -1 is_new = False #old vote if len(oldvote): v = oldvote[0] oldamount = int(v._name) v._name = str(amount) #these still need to be recalculated old_valid_thing = getattr(v, 'valid_thing', False) v.valid_thing = (valid_thing(v, karma, cheater=cheater) and getattr(v, 'valid_thing', False)) v.valid_user = (getattr(v, 'valid_user', False) and v.valid_thing and valid_user(v, sr, karma)) #new vote else: is_new = True oldamount = 0 v = rel(sub, obj, str(amount)) v.ip = ip old_valid_thing = v.valid_thing = valid_thing(v, karma, cheater=cheater) v.valid_user = (v.valid_thing and valid_user(v, sr, karma) and not is_self_link) if organic: v.organic = organic v._commit() up_change, down_change = score_changes(amount, oldamount) if not (is_new and obj.author_id == sub._id and amount == 1): # we don't do this if it's the author's initial automatic # vote, because we checked it in with _ups == 1 update_score(obj, up_change, down_change, v, old_valid_thing) if v.valid_user: author = Account._byID(obj.author_id, data=True) author.incr_karma(kind, sr, up_change - down_change) #update the sr's valid vote count if is_new and v.valid_thing and kind == 'link': if sub._id != obj.author_id: incr_sr_count(sr) # now write it out to Cassandra. We'll write it out to both # this way for a while CassandraVote._copy_from(v) queries.changed(v._thing2, True) return v
def add_props(cls, user, wrapped): from r2.lib.db import queries #TODO global-ish functions that shouldn't be here? #reset msgtime after this request msgtime = c.have_messages # make sure there is a sr_id set: for w in wrapped: if not hasattr(w, "sr_id"): w.sr_id = None # load the to fields if one exists to_ids = set(w.to_id for w in wrapped if w.to_id is not None) tos = Account._byID(to_ids, True) if to_ids else {} # load the subreddit field if one exists: sr_ids = set(w.sr_id for w in wrapped if w.sr_id is not None) m_subreddits = Subreddit._byID(sr_ids, data=True, return_dict=True) # load the links and their subreddits (if comment-as-message) links = Link._byID(set(l.link_id for l in wrapped if l.was_comment), data=True, return_dict=True) # subreddits of the links (for comment-as-message) l_subreddits = Subreddit._byID(set(l.sr_id for l in links.values()), data=True, return_dict=True) parents = Comment._byID(set(l.parent_id for l in wrapped if l.parent_id and l.was_comment), data=True, return_dict=True) # load the unread list to determine message newness unread = set(queries.get_unread_inbox(user)) msg_srs = set( m_subreddits[x.sr_id] for x in wrapped if x.sr_id is not None and isinstance(x.lookups[0], Message)) # load the unread mod list for the same reason mod_unread = set( queries.merge_results( *[queries.get_unread_subreddit_messages(sr) for sr in msg_srs])) for item in wrapped: item.to = tos.get(item.to_id) if item.sr_id: item.recipient = (item.author_id != c.user._id) else: item.recipient = (item.to_id == c.user._id) # new-ness is stored on the relation if item.author_id == c.user._id: item.new = False elif item._fullname in unread: item.new = True # wipe new messages if preferences say so, and this isn't a feed # and it is in the user's personal inbox if (item.new and c.user.pref_mark_messages_read and c.extension not in ("rss", "xml", "api", "json")): queries.set_unread(item.lookups[0], c.user, False) else: item.new = (item._fullname in mod_unread) item.score_fmt = Score.none item.message_style = "" # comment as message: if item.was_comment: link = links[item.link_id] sr = l_subreddits[link.sr_id] item.to_collapse = False item.author_collapse = False item.link_title = link.title item.permalink = item.lookups[0].make_permalink(link, sr=sr) item.link_permalink = link.make_permalink(sr) if item.parent_id: item.subject = _('comment reply') item.message_style = "comment-reply" parent = parents[item.parent_id] item.parent = parent._fullname item.parent_permalink = parent.make_permalink(link, sr) else: item.subject = _('post reply') item.message_style = "post-reply" elif item.sr_id is not None: item.subreddit = m_subreddits[item.sr_id] item.is_collapsed = None if not item.new: if item.recipient: item.is_collapsed = item.to_collapse if item.author_id == c.user._id: item.is_collapsed = item.author_collapse if c.user.pref_collapse_read_messages: item.is_collapsed = (item.is_collapsed is not False) if item.author_id in c.user.enemies and not item.was_comment: item.is_collapsed = True if not c.user_is_admin: item.subject = _('[message from blocked user]') item.body = _('[unblock user to see this message]') # Run this last Printable.add_props(user, wrapped)
def add_props(cls, user, wrapped): from r2.lib.db import queries #TODO global-ish functions that shouldn't be here? #reset msgtime after this request msgtime = c.have_messages # make sure there is a sr_id set: for w in wrapped: if not hasattr(w, "sr_id"): w.sr_id = None # load the to fields if one exists to_ids = set(w.to_id for w in wrapped if w.to_id is not None) tos = Account._byID(to_ids, True) if to_ids else {} # load the subreddit field if one exists: sr_ids = set(w.sr_id for w in wrapped if w.sr_id is not None) m_subreddits = Subreddit._byID(sr_ids, data=True, return_dict=True) # load the links and their subreddits (if comment-as-message) links = Link._byID( set(l.link_id for l in wrapped if l.was_comment), data=True, return_dict=True) # subreddits of the links (for comment-as-message) l_subreddits = Subreddit._byID( set(l.sr_id for l in links.values()), data=True, return_dict=True) parents = Comment._byID( set(l.parent_id for l in wrapped if l.parent_id and l.was_comment), data=True, return_dict=True) # load the unread list to determine message newness unread = set(queries.get_unread_inbox(user)) msg_srs = set( m_subreddits[x.sr_id] for x in wrapped if x.sr_id is not None and isinstance(x.lookups[0], Message)) # load the unread mod list for the same reason mod_unread = set( queries.merge_results( *[queries.get_unread_subreddit_messages(sr) for sr in msg_srs])) for item in wrapped: item.to = tos.get(item.to_id) if item.sr_id: item.recipient = (item.author_id != c.user._id) else: item.recipient = (item.to_id == c.user._id) # new-ness is stored on the relation if item.author_id == c.user._id: item.new = False elif item._fullname in unread: item.new = True # wipe new messages if preferences say so, and this isn't a feed # and it is in the user's personal inbox if (item.new and c.user.pref_mark_messages_read and c.extension not in ("rss", "xml", "api", "json")): queries.set_unread(item.lookups[0], c.user, False) else: item.new = (item._fullname in mod_unread) item.score_fmt = Score.none item.message_style = "" # comment as message: if item.was_comment: link = links[item.link_id] sr = l_subreddits[link.sr_id] item.to_collapse = False item.author_collapse = False item.link_title = link.title item.permalink = item.lookups[0].make_permalink(link, sr=sr) item.link_permalink = link.make_permalink(sr) if item.parent_id: item.subject = _('comment reply') item.message_style = "comment-reply" parent = parents[item.parent_id] item.parent = parent._fullname item.parent_permalink = parent.make_permalink(link, sr) else: item.subject = _('post reply') item.message_style = "post-reply" elif item.sr_id is not None: item.subreddit = m_subreddits[item.sr_id] if c.user.pref_no_profanity: item.subject = profanity_filter(item.subject) item.is_collapsed = None if not item.new: if item.recipient: item.is_collapsed = item.to_collapse if item.author_id == c.user._id: item.is_collapsed = item.author_collapse if c.user.pref_collapse_read_messages: item.is_collapsed = (item.is_collapsed is not False) # Run this last Printable.add_props(user, wrapped)
def _new(cls, author, to, subject, body, ip, parent=None, sr=None): m = Message( subject=subject, body=body, author_id=author._id, new=True, ip=ip) m._spam = author._spam sr_id = None # check to see if the recipient is a subreddit and swap args accordingly if to and isinstance(to, Subreddit): to_subreddit = True to, sr = None, to else: to_subreddit = False if sr: sr_id = sr._id if parent: m.parent_id = parent._id if parent.first_message: m.first_message = parent.first_message else: m.first_message = parent._id if parent.sr_id: sr_id = parent.sr_id if not to and not sr_id: raise CreationError, "Message created with neither to nor sr_id" m.to_id = to._id if to else None if sr_id is not None: m.sr_id = sr_id m._commit() inbox_rel = None if sr_id and not sr: sr = Subreddit._byID(sr_id) inbox_rel = [] if sr_id: # if there is a subreddit id, and it's either a reply or # an initial message to an SR, add to the moderator inbox # (i.e., don't do it for automated messages from the SR) if parent or to_subreddit: inbox_rel.append(ModeratorInbox._add(sr, m, 'inbox')) if author.name in g.admins: m.distinguished = 'admin' m._commit() elif sr.is_moderator(author): m.distinguished = 'yes' m._commit() # if there is a "to" we may have to create an inbox relation as well # also, only global admins can be message spammed. if to and (not m._spam or to.name in g.admins): # if the current "to" is not a sr moderator, # they need to be notified if not sr_id or not sr.is_moderator(to): inbox_rel.append(Inbox._add(to, m, 'inbox')) # find the message originator elif sr_id and m.first_message: first = Message._byID(m.first_message, True) orig = Account._byID(first.author_id, True) # if the originator is not a moderator... if not sr.is_moderator(orig) and orig._id != author._id: inbox_rel.append(Inbox._add(orig, m, 'inbox')) return (m, inbox_rel)
def vote(cls, sub, obj, dir, ip, organic=False, cheater=False): from admintools import valid_user, valid_thing, update_score from r2.lib.count import incr_counts from r2.lib.db import queries sr = obj.subreddit_slow kind = obj.__class__.__name__.lower() karma = sub.karma(kind, sr) is_self_link = (kind == 'link' and hasattr(obj, 'is_self') and obj.is_self) #check for old vote rel = cls.rel(sub, obj) oldvote = rel._fast_query(sub, obj, ['-1', '0', '1']).values() oldvote = filter(None, oldvote) amount = 1 if dir is True else 0 if dir is None else -1 is_new = False #old vote if len(oldvote): v = oldvote[0] oldamount = int(v._name) v._name = str(amount) #these still need to be recalculated old_valid_thing = v.valid_thing v.valid_thing = (valid_thing(v, karma, cheater=cheater) and v.valid_thing) v.valid_user = (v.valid_user and v.valid_thing and valid_user(v, sr, karma)) #new vote else: is_new = True oldamount = 0 v = rel(sub, obj, str(amount)) v.author_id = obj.author_id v.sr_id = sr._id v.ip = ip old_valid_thing = v.valid_thing = \ valid_thing(v, karma, cheater = cheater) v.valid_user = (v.valid_thing and valid_user(v, sr, karma) and not is_self_link) if organic: v.organic = organic v._commit() v._fast_query_timestamp_touch(sub) up_change, down_change = score_changes(amount, oldamount) if not (is_new and obj.author_id == sub._id and amount == 1): # we don't do this if it's the author's initial automatic # vote, because we checked it in with _ups == 1 update_score(obj, up_change, down_change, v.valid_thing, old_valid_thing) if v.valid_user: author = Account._byID(obj.author_id, data=True) author.incr_karma(kind, sr, up_change - down_change) #update the sr's valid vote count if is_new and v.valid_thing and kind == 'link': if sub._id != obj.author_id: incr_counts([sr]) # now write it out to Cassandra. We'll write it out to both # this way for a while voter = v._thing1 votee = v._thing2 cvc = CassandraVote._rel(Account, votee.__class__) try: cv = cvc._fast_query(voter._id36, votee._id36) except tdb_cassandra.NotFound: cv = cvc(thing1_id=voter._id36, thing2_id=votee._id36) cv.name = v._name cv.valid_user, cv.valid_thing = v.valid_user, v.valid_thing cv.ip = v.ip if getattr(v, 'organic', False) or hasattr(cv, 'organic'): cv.organic = getattr(v, 'organic', False) cv._commit() queries.changed(votee, True) return v
def author(self): return Account._byID(self.author_id, True)
def recipient(self): return Account._byID(self.recipient_id, data=True)
def vote(cls, sub, obj, dir, ip, spam = False, organic = False): from admintools import valid_user, valid_thing, update_score from r2.lib.count import incr_counts # An account can only perform 1 voting operation at a time. with g.make_lock('account_%s_voting' % sub._id): # If downvoting ensure that the user has enough karma, it # will raise an exception if not. if dir == False: sub.check_downvote() # Do the voting. sr = obj.subreddit_slow kind = obj.__class__.__name__.lower() karma = sub.karma(kind, sr) #check for old vote rel = cls.rel(sub, obj) oldvote = list(rel._query(rel.c._thing1_id == sub._id, rel.c._thing2_id == obj._id, data = True)) amount = 1 if dir is True else 0 if dir is None else -1 is_new = False #old vote if len(oldvote): v = oldvote[0] oldamount = int(v._name) v._name = str(amount) #these still need to be recalculated old_valid_thing = getattr(v, 'valid_thing', True) v.valid_thing = (valid_thing(v, karma) and v.valid_thing and not spam) v.valid_user = (v.valid_user and v.valid_thing and valid_user(v, sr, karma)) #new vote else: is_new = True oldamount = 0 v = rel(sub, obj, str(amount)) v.author_id = obj.author_id v.ip = ip old_valid_thing = v.valid_thing = (valid_thing(v, karma) and not spam) v.valid_user = v.valid_thing and valid_user(v, sr, karma) if organic: v.organic = organic v._commit() # Record that this account has made a downvote and # immediately release the lock since both the downvote # count and the vote have been updated. up_change, down_change = score_changes(amount, oldamount) if down_change: sub.incr_downvote(down_change) # Continue by updating karmas. update_score(obj, up_change, down_change, v.valid_thing, old_valid_thing) if v.valid_user: author = Account._byID(obj.author_id, data=True) author.incr_karma(kind, sr, up_change - down_change) #update the sr's valid vote count if is_new and v.valid_thing and kind == 'link': if sub._id != obj.author_id: incr_counts([sr]) return v
def add_props(cls, user, wrapped): from r2.lib.template_helpers import add_attr, get_domain from r2.lib import promote from r2.lib.wrapped import CachedVariable from r2.lib.pages import WrappedUser #fetch parent links links = Link._byID(set(l.link_id for l in wrapped), data=True, return_dict=True, stale=True) # fetch authors authors = Account._byID(set(l.author_id for l in links.values()), data=True, return_dict=True, stale=True) #get srs for comments that don't have them (old comments) for cm in wrapped: if not hasattr(cm, 'sr_id'): cm.sr_id = links[cm.link_id].sr_id subreddits = Subreddit._byID(set(cm.sr_id for cm in wrapped), data=True, return_dict=False, stale=True) cids = dict((w._id, w) for w in wrapped) parent_ids = set( cm.parent_id for cm in wrapped if getattr(cm, 'parent_id', None) and cm.parent_id not in cids) parents = {} if parent_ids: parents = Comment._byID(parent_ids, data=True, stale=True) can_reply_srs = set(s._id for s in subreddits if s.can_comment(user)) \ if c.user_is_loggedin else set() can_reply_srs.add(promote.get_promote_srid()) min_score = user.pref_min_comment_score profilepage = c.profilepage user_is_admin = c.user_is_admin user_is_loggedin = c.user_is_loggedin focal_comment = c.focal_comment cname = c.cname site = c.site for item in wrapped: # for caching: item.profilepage = c.profilepage item.link = links.get(item.link_id) if (item.link._score <= 1 or item.score < 3 or item.link._spam or item._spam or item.author._spam): item.nofollow = True else: item.nofollow = False if not hasattr(item, 'subreddit'): item.subreddit = item.subreddit_slow if item.author_id == item.link.author_id and not item.link._deleted: add_attr(item.attribs, 'S', link=item.link.make_permalink(item.subreddit)) if not hasattr(item, 'target'): item.target = "_top" if cname else None if item.parent_id: if item.parent_id in cids: item.parent_permalink = '#' + utils.to36(item.parent_id) else: parent = parents[item.parent_id] item.parent_permalink = parent.make_permalink( item.link, item.subreddit) else: item.parent_permalink = None item.can_reply = False if c.can_reply or (item.sr_id in can_reply_srs): age = datetime.now(g.tz) - item._date if age.days < g.REPLY_AGE_LIMIT: item.can_reply = True # not deleted on profile pages, # deleted if spam and not author or admin item.deleted = ( not profilepage and (item._deleted or (item._spam and item.author != user and not item.show_spam))) extra_css = '' if item.deleted: extra_css += "grayed" if not user_is_admin: item.author = DeletedUser() item.body = '[deleted]' if focal_comment == item._id36: extra_css += " border" if profilepage: item.link_author = WrappedUser(authors[item.link.author_id]) item.subreddit_path = item.subreddit.path if cname: item.subreddit_path = ("http://" + get_domain( cname=(site == item.subreddit), subreddit=False)) if site != item.subreddit: item.subreddit_path += item.subreddit.path item.full_comment_path = item.link.make_permalink(item.subreddit) # don't collapse for admins, on profile pages, or if deleted item.collapsed = False if ((item.score < min_score) and not (profilepage or item.deleted or user_is_admin)): item.collapsed = True item.collapsed_reason = _("comment score below threshold") if user_is_loggedin and item.author_id in c.user.enemies: if "grayed" not in extra_css: extra_css += " grayed" item.collapsed = True item.collapsed_reason = _("blocked user") item.editted = getattr(item, "editted", False) item.render_css_class = "comment %s" % CachedVariable( "time_period") #will get updated in builder item.num_children = 0 item.score_fmt = Score.points item.permalink = item.make_permalink(item.link, item.subreddit) item.is_author = (user == item.author) item.is_focal = (focal_comment == item._id36) item_age = c.start_time - item._date if item_age.days > g.VOTE_AGE_LIMIT: item.votable = False else: item.votable = True #will seem less horrible when add_props is in pages.py from r2.lib.pages import UserText item.usertext = UserText(item, item.body, editable=item.is_author, nofollow=item.nofollow, target=item.target, extra_css=extra_css) # Run this last Printable.add_props(user, wrapped)
def cast_vote(sub, obj, vote_info, timer, date): from r2.models.admintools import valid_user, valid_thing, update_score from r2.lib.count import incr_sr_count names_by_dir = {True: "1", None: "0", False: "-1"} # `vote` mimics the old pg vote rel interface so downstream code doesn't # need to change. (but it totally needn't stay that way forever!) vote = Storage( _thing1=sub, _thing2=obj, _name=names_by_dir[vote_info["dir"]], _date=date, valid_thing=True, valid_user=True, ip=vote_info["ip"], ) # these track how much ups/downs should change on `obj` ups_delta = 1 if int(vote._name) > 0 else 0 downs_delta = 1 if int(vote._name) < 0 else 0 # see if the user has voted on this thing before old_votes = VoteDetailsByThing.get_details(obj, [sub]) old_vote = None if old_votes: old_vote = old_votes[0] timer.intermediate("cass_read_vote") if old_vote: vote._date = datetime.utcfromtimestamp( old_vote["date"]).replace(tzinfo=pytz.UTC) vote.valid_thing = old_vote["valid_thing"] vote.valid_user = old_vote["valid_user"] vote.ip = old_vote["ip"] if vote._name == old_vote["direction"]: # the old vote and new vote are the same. bail out. return vote # remove the old vote from the score old_direction = int(old_vote["direction"]) ups_delta -= 1 if old_direction > 0 else 0 downs_delta -= 1 if old_direction < 0 else 0 # calculate valid_thing and valid_user sr = obj.subreddit_slow kind = obj.__class__.__name__.lower() karma = sub.karma(kind, sr) if vote.valid_thing: vote.valid_thing = valid_thing(vote, karma, vote_info["cheater"], vote_info["info"]) if vote.valid_user: vote.valid_user = vote.valid_thing and valid_user(vote, sr, karma) if kind == "link" and getattr(obj, "is_self", False): # self-posts do not generate karma vote.valid_user = False g.stats.simple_event("vote.valid_thing." + str(vote.valid_thing).lower()) g.stats.simple_event("vote.valid_user." + str(vote.valid_user).lower()) # update various score/karma/vote counts if not (not old_vote and obj.author_id == sub._id and vote._name == "1"): # newly created objects start out with _ups = 1, so we skip updating # their score here if this is the author's own initial vote on it. old_valid_thing = old_vote["valid_thing"] if old_vote else True update_score(obj, ups_delta, downs_delta, vote, old_valid_thing) timer.intermediate("pg_update_score") if vote.valid_user: author = Account._byID(obj.author_id, data=True) author.incr_karma(kind, sr, ups_delta - downs_delta) timer.intermediate("pg_incr_karma") if not old_vote and vote.valid_thing and kind == "link": if sub._id != obj.author_id: incr_sr_count(sr) timer.intermediate("incr_sr_counts") # write the vote to cassandra VotesByAccount.copy_from(vote, vote_info["info"]) timer.intermediate("cassavotes") vote._thing2.update_search_index(boost_only=True) timer.intermediate("update_search_index") if "event" in vote_info and vote_info["event"]: g.events.vote_event(vote, old_vote, event_base=vote_info["event"]) return vote
def link_author(self): return Account._byID(self.link.author_id, data=True)