Delete the jury system.

This commit is contained in:
bsimpson63
2012-10-21 11:28:31 -04:00
parent a96995954c
commit 42ed57a24c
23 changed files with 14 additions and 944 deletions

View File

@@ -115,8 +115,6 @@ def make_map():
mc('/prefs/:location', controller='forms', action='prefs',
location='options')
mc('/depmod', controller='forms', action='depmod')
mc('/info/0:article/*rest', controller='front',
action='oldinfo', dest='comments', type='ancient')
mc('/info/:article/:dest/:comment', controller='front',

View File

@@ -42,7 +42,6 @@ from r2.lib.pages import (EnemyList, FriendList, ContributorList, ModList,
from r2.lib.pages import FlairList, FlairCsv, FlairTemplateEditor, \
FlairSelector
from r2.lib.pages import PrefApps
from r2.lib.utils.trial_utils import indict, trial_info
from r2.lib.pages.things import wrap_links, default_thing_wrapper
from r2.models.last_modified import LastModified
@@ -952,15 +951,6 @@ class ApiController(RedditController, OAuth2ResourceController):
return
c.user.add_enemy(block_acct)
@noresponse(VAdmin(), VModhash(),
thing = VByName('id'))
def POST_indict(self, thing):
'''put something on trial'''
if not thing:
log_text("indict: no thing", level="warning")
indict(thing)
@require_oauth2_scope("edit")
@validatedForm(VUser(),
VModhash(),
@@ -1157,41 +1147,6 @@ class ApiController(RedditController, OAuth2ResourceController):
VRatelimit.ratelimit(rate_user=True, rate_ip = True,
prefix = "rate_share_")
@noresponse(VUser(),
VModhash(),
ip = ValidIP(),
dir = VInt('dir', min=-1, max=1),
thing = VByName('id'))
def POST_juryvote(self, dir, thing, ip):
if not thing:
log_text("juryvote: no thing", level="warning")
return
if not ip:
log_text("juryvote: no ip", level="warning")
return
if dir is None:
log_text("juryvote: no dir", level="warning")
return
j = Jury.by_account_and_defendant(c.user, thing)
if not trial_info([thing]).get(thing._fullname,False):
log_text("juryvote: not on trial", level="warning")
return
if not j:
log_text("juryvote: not on the jury", level="warning")
return
log_text("juryvote",
"%s cast a %d juryvote on %s" % (c.user.name, dir, thing._id36),
level="info")
j._name = str(dir)
j._date = c.start_time
j._commit()
@require_oauth2_scope("vote")
@noresponse(VUser(),

View File

@@ -492,9 +492,6 @@ class FrontController(RedditController, OAuth2ResourceController):
query = c.site.get_reported()
elif location == 'spam':
query = c.site.get_spam()
elif location == 'trials':
query = c.site.get_trials()
num = 1000
elif location == 'modqueue':
query = c.site.get_modqueue()
elif location == 'unmoderated':
@@ -518,14 +515,12 @@ class FrontController(RedditController, OAuth2ResourceController):
return x.reported > 0 and not x._spam
elif location == "spam":
return x._spam
elif location == "trials":
return not getattr(x, "verdict", None)
elif location == "modqueue":
if x.reported > 0 and not x._spam:
return True # reported but not banned
verdict = getattr(x, "verdict", None)
if verdict is None:
return True # anything without a verdict (i.e., trials)
return True # anything without a verdict
if x._spam and verdict != 'mod-removed':
return True # spam, unless banned by a moderator
return False
@@ -564,11 +559,8 @@ class FrontController(RedditController, OAuth2ResourceController):
else:
raise ValueError
if ((level == 'mod' and
location in ('reports', 'spam', 'trials', 'modqueue', 'unmoderated'))
or
(level == 'all' and
location == 'trials')):
if (level == 'mod' and
location in ('reports', 'spam', 'modqueue', 'unmoderated')):
pane = self._make_spamlisting(location, num, after, reverse, count)
if c.user.pref_private_feeds:
extension_handling = "private"
@@ -624,7 +616,7 @@ class FrontController(RedditController, OAuth2ResourceController):
stylesheet = (c.site.stylesheet_contents_user or
c.site.stylesheet_contents)
pane = SubredditStylesheetSource(stylesheet_contents=stylesheet)
elif (location in ('reports', 'spam', 'trials', 'modqueue', 'unmoderated')
elif (location in ('reports', 'spam', 'modqueue', 'unmoderated')
and is_moderator):
c.allow_styles = True
pane = self._make_spamlisting(location, num, after, reverse, count)
@@ -1134,55 +1126,6 @@ class FormsController(RedditController):
return BoringPage(_("reset password"),
content=ResetPassword(key=key, done=done)).render()
@validate(VUser())
def GET_depmod(self):
displayPane = PaneStack()
active_trials = {}
finished_trials = {}
juries = Jury.by_account(c.user)
trials = trial_info([j._thing2 for j in juries])
for j in juries:
defendant = j._thing2
if trials.get(defendant._fullname, False):
active_trials[defendant._fullname] = j._name
else:
finished_trials[defendant._fullname] = j._name
if active_trials:
fullnames = sorted(active_trials.keys(), reverse=True)
def my_wrap(thing):
w = Wrapped(thing)
w.hide_score = True
w.likes = None
w.trial_mode = True
w.render_class = LinkOnTrial
w.juryvote = active_trials[thing._fullname]
return w
listing = wrap_links(fullnames, wrapper=my_wrap)
displayPane.append(InfoBar(strings.active_trials,
extra_class="mellow"))
displayPane.append(listing)
if finished_trials:
fullnames = sorted(finished_trials.keys(), reverse=True)
listing = wrap_links(fullnames)
displayPane.append(InfoBar(strings.finished_trials,
extra_class="mellow"))
displayPane.append(listing)
displayPane.append(InfoBar(strings.more_info_link %
dict(link="/help/deputies"),
extra_class="mellow"))
return Reddit(content = displayPane).render()
@validate(VUser(),
location = nop("location"))
def GET_prefs(self, location=''):

View File

@@ -20,7 +20,7 @@
# Inc. All Rights Reserved.
###############################################################################
from r2.models import Account, Link, Comment, Trial, Vote, SaveHide, Report
from r2.models import Account, Link, Comment, Vote, SaveHide, Report
from r2.models import Message, Inbox, Subreddit, ModContribSR, ModeratorInbox, MultiReddit
from r2.lib.db.thing import Thing, Merge
from r2.lib.db.operators import asc, desc, timeago
@@ -459,19 +459,6 @@ class QueryishList(list):
else:
raise StopIteration
def get_trials_links(sr):
l = Trial.defendants_by_sr(sr)
s = QueryishList(l)
s._sort = [db_sort('new')]
return s
def get_trials(sr):
if isinstance(sr, (ModContribSR, MultiReddit)):
srs = Subreddit._byID(sr.sr_ids, return_dict=False)
return get_trials_links(srs)
else:
return get_trials_links(sr)
@merged_cached_query
def get_modqueue(sr):
q = []

View File

@@ -35,10 +35,9 @@ class PrintableButtons(Styled):
def __init__(self, style, thing,
show_delete = False, show_report = True,
show_distinguish = False, show_marknsfw = False,
show_unmarknsfw = False, show_indict = False, is_link=False,
show_unmarknsfw = False, is_link=False,
show_flair = False, **kw):
show_ignore = (thing.show_reports or
(thing.reveal_trial_info and not thing.show_spam))
show_ignore = thing.show_reports
approval_checkmark = getattr(thing, "approval_checkmark", None)
show_approve = (thing.show_spam or show_ignore or
(is_link and approval_checkmark is None)) and not thing._deleted
@@ -54,7 +53,6 @@ class PrintableButtons(Styled):
show_delete = show_delete,
show_approve = show_approve,
show_report = show_report,
show_indict = show_indict,
show_distinguish = show_distinguish,
show_marknsfw = show_marknsfw,
show_unmarknsfw = show_unmarknsfw,
@@ -78,9 +76,6 @@ class LinkButtons(PrintableButtons):
if c.user_is_admin and thing.promoted is None:
show_report = False
show_indict = True
else:
show_indict = False
if (thing.can_ban or is_author) and not thing.nsfw:
show_marknsfw = True
@@ -129,7 +124,6 @@ class LinkButtons(PrintableButtons):
hidden = thing.hidden,
show_delete = show_delete,
show_report = show_report and c.user_is_loggedin,
show_indict = show_indict,
show_distinguish = show_distinguish,
show_marknsfw = show_marknsfw,
show_unmarknsfw = show_unmarknsfw,

View File

@@ -87,8 +87,6 @@ string_dict = dict(
sr_created = _('your reddit has been created'),
active_trials = _("we haven't yet decided whether these things are spam, so you have a chance to change your vote:"),
finished_trials = _("it's too late to change your vote on these things (the verdict has been issued):"),
more_info_link = _("visit [%(link)s](%(link)s) for more information"),
sr_messages = dict(

View File

@@ -28,4 +28,4 @@ from cmd_utils import *
try:
from r2admin.lib.admin_utils import *
except ImportError:
from admin_utils import *
pass

View File

@@ -1,27 +0,0 @@
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer. The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2012 reddit
# Inc. All Rights Reserved.
###############################################################################
def jury_cache_dict(account, ip, slash16):
return {"recent-juror-key": 1}
def voir_dire_priv (account, ip, slash16, defendant, sr):
return True

View File

@@ -1,237 +0,0 @@
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer. The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2012 reddit
# Inc. All Rights Reserved.
###############################################################################
from pylons import c, g, request
from r2.lib.utils import jury_cache_dict, voir_dire_priv, tup
from r2.lib.memoize import memoize
from r2.lib.log import log_text
import random as rand
# Hardcache lifetime for a trial.
# The regular hardcache reaper should never run on one of these,
# since a mistrial should be declared if the trial is still open
# after 24 hours. So the "3 days" expiration isn't really real.
TRIAL_TIME = 3 * 86400
def trial_key(thing):
return "trial-" + thing._fullname
def trial_info(things):
things = tup(things)
keys = dict((trial_key(thing), thing._fullname)
for thing in things)
# TODO: disabling trial lookup for now, since there aren't any
# vals = g.hardcache.get_multi(keys)
vals = {}
return dict((keys[key], val)
for (key, val)
in vals.iteritems())
def end_trial(thing, verdict=None):
from r2.models import Trial
if trial_info(thing):
g.hardcache.delete(trial_key(thing))
Trial.all_defendants(_update=True)
if verdict is not None:
thing.verdict = verdict
thing._commit()
def indict(defendant):
from r2.models import Trial
tk = trial_key(defendant)
rv = False
if defendant._deleted:
result = "already deleted"
elif getattr(defendant, "promoted", None) is not None:
result = "it's promoted"
elif hasattr(defendant, "verdict") and defendant.verdict is not None:
result = "it already has a verdict"
elif g.hardcache.get(tk):
result = "it's already on trial"
else:
# The spams/koshers dict is just a infrequently-updated cache; the
# official source of the data is the Jury relation.
g.hardcache.set(tk, dict(spams=0, koshers=0), TRIAL_TIME)
Trial.all_defendants(_update=True)
result = "it's now indicted: %s" % tk
rv = True
log_text("indict_result", "%s: %s" % (defendant._id36, result), level="info")
return rv
# These are spam/kosher votes, not up/down votes
def update_voting(defendant, koshers, spams):
tk = trial_key(defendant)
d = g.hardcache.get(tk)
if d is None:
log_text("update_voting() fail",
"%s not on trial" % defendant._id36,
level="error")
else:
d["koshers"] = koshers
d["spams"] = spams
g.hardcache.set(tk, d, TRIAL_TIME)
# Check to see if a juror is eligible to serve on a jury for a given link.
def voir_dire(account, ip, slash16, defendants_assigned_to, defendant, sr):
from r2.models import Link
if defendant._deleted:
g.log.debug("%s is deleted" % defendant)
return False
if defendant._id in defendants_assigned_to:
g.log.debug("%s is already assigned to %s" % (account.name, defendant))
return False
if not isinstance(defendant, Link):
g.log.debug("%s can't serve on a jury for %s: it's not a link" %
account.name, defendant)
return False
if g.debug:
return True
if not voir_dire_priv(account, ip, slash16, defendant, sr):
return False
return True
def assign_trial(account, juries_already_on, ip, slash16):
from r2.models import Jury, Subreddit, Trial
from r2.lib.db import queries
defendants_assigned_to = []
for jury in juries_already_on:
defendants_assigned_to.append(jury._thing2_id)
subscribed_sr_ids = Subreddit.user_subreddits(account, ids=True, limit=None)
# Pull defendants, except ones which already have lots of juryvotes
defs = Trial.all_defendants(quench=True)
# Filter out defendants outside this user's subscribed SRs
defs = filter (lambda d: d.sr_id in subscribed_sr_ids, defs)
# Dictionary of sr_id => SR for all defendants' SRs
srs = Subreddit._byID(set([ d.sr_id for d in defs ]))
# Dictionary of sr_id => eligibility bool
submit_srs = {}
for sr_id, sr in srs.iteritems():
submit_srs[sr_id] = sr.can_submit(account) and not sr._spam
# Filter out defendants with ineligible SRs
defs = filter (lambda d: submit_srs.get(d.sr_id), defs)
likes = queries.get_likes(account, defs)
if not g.debug:
# Filter out things that the user has upvoted or downvoted
defs = filter (lambda d: likes.get((account, d)) is None, defs)
# Prefer oldest trials
defs.sort(key=lambda x: x._date)
for defendant in defs:
sr = srs[defendant.sr_id]
if voir_dire(account, ip, slash16, defendants_assigned_to, defendant, sr):
j = Jury._new(account, defendant)
return defendant
return None
def populate_spotlight():
raise Exception("this function is broken (re: ip_and_slash16) and pending demolition")
from r2.models import Jury
from r2.lib.db.thing import NotFound
if not (c.user_is_loggedin and c.user.jury_betatester()):
g.log.debug("not eligible")
return None
try:
juries_already_on = Jury.by_account(c.user)
except NotFound:
# This can happen if Jury.delete_old() just so happens to be cleaning
# up this user's old Jury rels while they're visiting the front page.
# In this unlucky case, just skip the 20% nagging below.
juries_already_on = []
# If they're already on a jury, and haven't yet voted, re-show
# it every five or so times.
if rand.random() < 0.2:
unvoted = filter(lambda j: j._name == '0', juries_already_on)
defs = [u._thing2 for u in unvoted]
active_trials = trial_info(defs)
for d in defs:
if active_trials.get(d._fullname, False):
return d
if not g.cache.add("global-jury-key", True, 5):
g.log.debug("not yet time to add another juror")
return None
ip, slash16 = ip_and_slash16(request)
jcd = jury_cache_dict(c.user, ip, slash16)
if jcd is None:
g.cache.delete("global-jury-key")
return None
if g.cache.get_multi(jcd.keys()) and not g.debug:
g.log.debug("recent juror")
g.cache.delete("global-jury-key")
return None
trial = assign_trial(c.user, juries_already_on, ip, slash16)
if trial is None:
g.log.debug("nothing available")
g.cache.delete("global-jury-key")
return None
for k, v in jcd.iteritems():
g.cache.set(k, True, v)
log_text("juryassignment",
"%s was just assigned to the jury for %s" % (c.user.name, trial._id36),
level="info")
return trial
def look_for_verdicts():
from r2.models import Trial, Jury
print "checking all trials for verdicts..."
for defendant in Trial.all_defendants():
print "Looking at reddit.com/comments/%s/x" % defendant._id36
v = Trial(defendant).check_verdict()
print "Verdict: %r" % v
Jury.delete_old(verbose=True, limit=1000)

View File

@@ -29,8 +29,6 @@ from report import *
from subreddit import *
from flair import *
from award import *
from jury import *
from trial import *
from ad import *
from bidding import *
from mail_queue import Email, has_opted_out, opt_count

View File

@@ -177,12 +177,6 @@ class Account(Thing):
# Legacy, None means user may wiki
return True
return self.wiki_override
def jury_betatester(self):
if g.cache.get("jury-killswitch"):
return False
else:
return True
def all_karmas(self):
"""returns a list of tuples in the form (name, hover-text, link_karma,

View File

@@ -369,10 +369,6 @@ def ip_span(ip):
return '<!-- %s -->' % ip
def filter_quotas(unfiltered):
from r2.lib.utils.trial_utils import trial_info
trials = trial_info(unfiltered)
now = datetime.now(g.tz)
baskets = {
@@ -408,9 +404,7 @@ def filter_quotas(unfiltered):
'admin-approved', 'mod-approved')
# Then, make sure it's worthy of quota-clogging
if trials.get(item._fullname):
pass
elif item._spam:
if item._spam:
pass
elif item._deleted:
pass

View File

@@ -228,7 +228,6 @@ class Builder(object):
w.show_reports = False
w.show_spam = False
w.can_ban = False
w.reveal_trial_info = False
w.use_big_modbuttons = False
if (c.user_is_admin

View File

@@ -1,103 +0,0 @@
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer. The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2012 reddit
# Inc. All Rights Reserved.
###############################################################################
from r2.lib.db.thing import DataThing, Thing, MultiRelation, Relation
from r2.lib.db.thing import NotFound, load_things
from r2.lib.db.userrel import UserRel
from r2.lib.db.operators import asc, desc, lower
from r2.lib.memoize import memoize
from r2.lib.utils import timeago
from r2.models import Account, Link
from pylons import c, g, request
class Jury(MultiRelation('jury',
Relation(Account, Link))):
@classmethod
def _new(cls, account, defendant):
j = Jury(account, defendant, "0")
j._commit()
Jury.by_account(account, _update=True)
Jury.by_defendant(defendant, _update=True)
return j
@classmethod
@memoize('jury.by_account')
def by_account_cache(cls, account_id):
q = cls._query(cls.c._thing1_id == account_id)
q._limit = 100
return [ j._fullname for j in q ]
@classmethod
def by_account(cls, account, _update=False):
rel_ids = cls.by_account_cache(account._id, _update=_update)
juries = DataThing._by_fullname(rel_ids, data=True,
return_dict = False)
if juries:
load_things(juries, load_data=True)
return juries
@classmethod
@memoize('jury.by_defendant')
def by_defendant_cache(cls, defendant_id):
q = cls._query(cls.c._thing2_id == defendant_id)
q._limit = 1000
return [ j._fullname for j in q ]
@classmethod
def by_defendant(cls, defendant, _update=False):
rel_ids = cls.by_defendant_cache(defendant._id, _update=_update)
juries = DataThing._by_fullname(rel_ids, data=True,
return_dict = False)
if juries:
load_things(juries, load_data=True)
return juries
@classmethod
def by_account_and_defendant(cls, account, defendant):
q = cls._fast_query(account, defendant, ("-1", "0", "1"))
v = filter(None, q.values())
if v:
return v[0]
@classmethod
def delete_old(cls, age="3 days", limit=500, verbose=False):
cutoff = timeago(age)
q = cls._query(cls.c._date < cutoff)
q._limit = limit
accounts = set()
defendants = set()
for j in q:
accounts.add(j._thing1)
defendants.add(j._thing2)
j._delete()
for a in accounts:
Jury.by_account(a, _update=True)
for d in defendants:
if verbose:
print "Deleting juries for defendant %s" % d._fullname
Jury.by_defendant(d, _update=True)

View File

@@ -24,7 +24,6 @@ from r2.lib.db.thing import Thing, Relation, NotFound, MultiRelation, \
CreationError
from r2.lib.db.operators import desc
from r2.lib.utils import base_url, tup, domain, title_to_url, UrlParser
from r2.lib.utils.trial_utils import trial_info
from account import Account, DeletedUser
from subreddit import Subreddit, DomainSR
from printable import Printable
@@ -351,8 +350,6 @@ class Link(Thing, Printable):
else:
saved = hidden = clicked = {}
trials = trial_info(wrapped)
for item in wrapped:
show_media = False
if not hasattr(item, "score_fmt"):
@@ -507,8 +504,6 @@ class Link(Thing, Printable):
item.author = DeletedUser()
item.as_deleted = True
item.trial_info = trials.get(item._fullname, None)
item.approval_checkmark = None
item_age = datetime.now(g.tz) - item._date
@@ -529,10 +524,6 @@ class Link(Thing, Printable):
else:
item.approval_checkmark = _("approved by a moderator")
if item.trial_info is not None:
item.reveal_trial_info = True
item.use_big_modbuttons = True
item.expunged = False
if item.is_self:
item.expunged = Link._should_expunge_selftext(item)
@@ -1364,14 +1355,6 @@ class Inbox(MultiRelation('inbox',
res.append(i)
return res
class LinkOnTrial(Printable):
@classmethod
def add_props(cls, user, wrapped):
Link.add_props(user, wrapped)
for item in wrapped:
item.rowstyle = "link ontrial"
# Run this last
Printable.add_props(user, wrapped)
class ModeratorInbox(Relation(Subreddit, Message)):
#TODO: shouldn't dupe this

View File

@@ -480,10 +480,6 @@ class Subreddit(Thing, Printable):
from r2.lib.db import queries
return queries.get_reported(self)
def get_trials(self):
from r2.lib.db import queries
return queries.get_trials(self)
def get_modqueue(self):
from r2.lib.db import queries
return queries.get_modqueue(self)

View File

@@ -1,211 +0,0 @@
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer. The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2012 reddit
# Inc. All Rights Reserved.
###############################################################################
from r2.models import Thing, Link, Subreddit, AllSR, admintools
from r2.lib.utils import Storage, tup
from r2.lib.memoize import memoize
from datetime import datetime
from pylons import g
class Trial(Storage):
def __init__(self, defendant):
from r2.lib.utils.trial_utils import trial_info
if not defendant._loaded:
defendant._load()
if not trial_info(defendant):
raise ValueError ("Defendant %s is not on trial" % defendant._id)
self.defendant = defendant
def convict(self, details = ''):
# if self.defendant._spam:
# TODO: PM submitter, maybe?
# else:
# TODO: PM submitter, maybe?
admintools.spam(self.defendant, auto=False, moderator_banned=True,
banner="deputy moderation" + details)
def acquit(self, details = ''):
admintools.unspam(self.defendant, unbanner="deputy moderation" + details)
# if self.defendant._spam:
# TODO: PM submitter
# TODO: reset submission time:
# self.defendant._date = datetime.now(g.tz)
def mistrial(self):
#TODO: PM mods
if self.defendant._spam:
pass #TODO: PM submitter
def verdict(self):
from r2.models import Jury
from r2.lib.utils.trial_utils import update_voting
koshers = 0
spams = 0
nones = 0
now = datetime.now(g.tz)
defendant_age = now - self.defendant._date
if defendant_age.days > 0:
return ("jury timeout", None, None)
latest_juryvote = None
for j in Jury.by_defendant(self.defendant):
if j._name == "0":
nones += 1
continue
# For non-zero votes, update latest_juryvote
if latest_juryvote is None:
latest_juryvote = j._date
else:
latest_juryvote = max(latest_juryvote, j._date)
if j._name == "1":
koshers += 1
elif j._name == "-1":
spams += 1
else:
raise ValueError("weird jury vote: [%s]" % j._name)
# The following trace is temporary; it'll be removed once this
# is done via cron job as opposed to manually
print "%d koshers, %d spams, %d haven't voted yet" % (koshers, spams, nones)
update_voting(self.defendant, koshers, spams)
total_votes = koshers + spams
if total_votes < 7:
g.log.debug("not enough votes yet")
return (None, koshers, spams)
# Stop showing this in the spotlight box once it has 20 votes
if total_votes >= 20:
g.cache.set("quench_jurors-" + self.defendant._fullname, True)
quenching = True
else:
quenching = False
# If a trial is less than an hour old, and votes are still trickling
# in (i.e., there was one in the past five minutes), we're going to
# require a nearly unanimous opinion to end the trial without
# waiting for more votes.
if defendant_age.seconds < 3600 and (now - latest_juryvote).seconds < 300:
trickling = True
else:
trickling = False
kosher_pct = float(koshers) / float(total_votes)
if kosher_pct < 0.13:
return ("guilty", koshers, spams)
elif kosher_pct > 0.86:
return ("innocent", koshers, spams)
elif trickling:
g.log.debug("votes still trickling in")
return (None, koshers, spams)
elif kosher_pct < 0.34:
return ("guilty", koshers, spams)
elif kosher_pct > 0.66:
return ("innocent", koshers, spams)
elif not quenching:
g.log.debug("not yet quenching")
return (None, koshers, spams)
# At this point, we're not showing the link to any new jurors, and
# the existing jurors haven't changed or submitted votes for several
# minutes, so we're not really expecting to get many more votes.
# Thus, lower our standards for consensus.
elif kosher_pct < 0.3999:
return ("guilty", koshers, spams)
elif kosher_pct > 0.6001:
return ("innocent", koshers, spams)
elif total_votes >= 100:
# This should never really happen; quenching should kick in
# after 20 votes, so new jurors won't be assigned to the
# trial. Just in case something goes wrong, close any trials
# with more than 100 votes.
return ("hung jury", koshers, spams)
else:
g.log.debug("hung jury, so far")
return (None, koshers, spams) # no decision yet; wait for more voters
def check_verdict(self):
from r2.lib.utils.trial_utils import end_trial
verdict, koshers, spams = self.verdict()
if verdict is None:
return # no verdict yet
if verdict in ("jury timeout", "hung jury"):
self.mistrial()
else:
details=", %d-%d" % (spams, koshers)
if verdict == "guilty":
self.convict(details)
elif verdict == "innocent":
self.acquit(details)
else:
raise ValueError("Invalid verdict [%s]" % verdict)
end_trial(self.defendant, verdict)
return verdict
@classmethod
@memoize('trial.all_defendants')
def all_defendants_cache(cls):
fnames = g.hardcache.backend.ids_by_category("trial")
return fnames
@classmethod
def all_defendants(cls, quench=False, _update=False):
all = cls.all_defendants_cache(_update=_update)
defs = Thing._by_fullname(all, data=True).values()
if quench:
# Used for the spotlight, to filter out trials with over 20 votes;
# otherwise, hung juries would hog the spotlight for an hour as
# their vote counts continued to skyrocket
return filter (lambda d:
not g.cache.get("quench_jurors-" + d._fullname),
defs)
else:
return defs
# sr can be plural
@classmethod
def defendants_by_sr(cls, sr):
all = cls.all_defendants()
if isinstance(sr, AllSR):
return all
sr = tup(sr)
sr_ids = [ s._id for s in sr ]
return filter (lambda x: x.sr_id in sr_ids, all)

View File

@@ -3223,20 +3223,12 @@ form input[type=radio] {margin: 2px .5em 0 0; }
border-radius: 3px;
}
.entry .buttons li.trial-stamp, .entry .buttons li.reported-stamp {
.entry .buttons li.reported-stamp {
border: 1px solid black !important;
padding: 0 4px;
background-color: #f6e69f;
}
.entry .buttons li.trial-stamp .spam {
color: #e00;
}
.entry .buttons li.trial-stamp .kosher {
color: #090;
}
.suspicious { background-color: #f6e69f }
.thing.spam { background-color: #FA8072 }
@@ -5202,62 +5194,6 @@ a.pretty-button.positive.pressed {
background-image: url(../bg-button-positive-pressed.png); /* SPRITE stretch-x */
}
/*
.jury-box .pretty-button a.pressed {
margin: -1px 4px -1px 1px;
border-width: 2px;
border-color: black;
color: white;
}
*/
.organic-listing .ontrial, .help-cover.juryduty {
background-color: #ffecf8;
}
.organic-listing .ontrial a.title {
color: #555;
}
.organic-listing .ontrial .midcol {
margin-top: 20px;
}
.we-need-help {
font-size: larger;
font-weight: bold;
line-height: 18px;
}
.organic-listing .how-to-classify {
font-weight: bold;
}
.jury-box .arrow-msg {
display: none;
color: red;
margin-top:5px;
}
.jury-box .thanks-for-voting {
margin-left: 1em;
display: none;
color: red;
}
.jury-box .thanks-for-voting a {
margin-left: 1em;
}
.sitetable .linkontrial {
/* border-top: solid 1px #ccc; */
padding-top: 5px;
}
.sitetable .we-need-help {
display: none;
}
.oatmeal img {
display: block;
margin: 5px auto;

View File

@@ -1193,24 +1193,6 @@ function big_mod_action(elem, dir) {
return false;
}
function juryvote(elem, dir) {
var thing_id = elem.thing_id();
if (elem.hasClass("pressed")) {
dir = 0;
}
elem.toggleClass("pressed");
elem.siblings(".pretty-button").removeClass("pressed");
d = {
id: thing_id,
dir: dir
};
$.request("juryvote", d, null, true);
elem.siblings(".thanks-for-voting").show();
return false;
}
/* The ready method */
$(function() {

View File

@@ -42,9 +42,7 @@
<%def name="make_link(name, css_class)">
<a class="${css_class} ${ c.user_is_loggedin and 'loggedin' or ''} ${thing.clicked and 'click' or ''}"
%if not (getattr(thing, "trial_mode", None) and thing.is_self):
href="${thing.href_url}"
%endif
href="${thing.href_url}"
%if thing.nofollow:
rel="nofollow"
%endif
@@ -133,8 +131,7 @@
%>
## if we're not on a permalink page we'll render the buttons on top
## (unless it's also a jury duty listing)
%if not (expand or getattr(thing, "trial_mode", None)):
%if not expand:
${bottom_buttons()}
%endif
@@ -195,8 +192,6 @@ ${parent.thing_data_attributes(what)} data-ups="${what.upvotes}" data-downs="${w
${self.arrow(thing, 1, thing.likes)}
%if thing.pref_compress:
<div class="score-placeholder"></div>
%elif getattr(thing, "trial_mode", None):
<div class="score unvoted">&bull;</div>
%elif thing.hide_score:
<div class="score likes">&bull;</div>
<div class="score unvoted">&bull;</div>
@@ -242,7 +237,7 @@ ${parent.thing_data_attributes(what)} data-ups="${what.upvotes}" data-downs="${w
</%def>
<%def name="thumbnail()">
%if thing.thumbnail and not getattr(thing, "trial_mode", None):
%if thing.thumbnail:
<%call expr="make_link('thumbnail', 'thumbnail ' + (thing.thumbnail if thing.thumbnail_sprited else ''))">
% if not thing.thumbnail_sprited:
<%

View File

@@ -1,81 +0,0 @@
## The contents of this file are subject to the Common Public Attribution
## License Version 1.0. (the "License"); you may not use this file except in
## compliance with the License. You may obtain a copy of the License at
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
## software over a computer network and provide for limited attribution for the
## Original Developer. In addition, Exhibit A has been modified to be
## consistent with Exhibit B.
##
## Software distributed under the License is distributed on an "AS IS" basis,
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
## the specific language governing rights and limitations under the License.
##
## The Original Code is reddit.
##
## The Original Developer is the Initial Developer. The Initial Developer of
## the Original Code is reddit Inc.
##
## All portions of the code written by reddit are Copyright (c) 2006-2012
## reddit Inc. All Rights Reserved.
###############################################################################
<%!
from r2.lib.pages.things import LinkButtons
from r2.lib.pages import WrappedUser
%>
<%inherit file="link.html"/>
<%namespace file="printablebuttons.html" import="ynbutton" />
<%namespace file="utils.html" import="plain_link" />
<%namespace file="utils.html" import="pretty_button" />
<%def name="buttons(comments=True, delete=True, report=True, additional='')">
</%def>
<%def name="arrow(this, dir, mod)">
<div class="arrow ${'up' if dir > 0 else 'down'}"
onclick="$(this).thing().find('.arrow-msg').show()">
</div>
</%def>
<%def name="entry()">
<span class="we-need-help">
${_("reddit's spam filter needs your help!")}
</span>
${parent.entry()}
<div class="jury-box">
<span class="how-to-classify">
${_("how would you classify this link?")}
</span>
<%
pos_class = "positive"
neg_class = "negative"
if not hasattr(thing, "juryvote"):
pass
elif int(thing.juryvote) == 1:
pos_class += " pressed"
elif int(thing.juryvote) == -1:
neg_class += " pressed"
%>
${pretty_button(_("off-topic / spam"), "juryvote", -1, neg_class)}
${pretty_button(_("kosher"), "juryvote", 1, pos_class)}
<span class="thanks-for-voting">
${_("thanks for voting!")}
<a href="/help/deputies">
${_("Click here for more info.").lower()}
</a>
</span>
<div class="arrow-msg">
${_("those arrows are only for show; they don't actually do anything.")}
</div>
</div>
</%def>

View File

@@ -132,7 +132,7 @@
<tr>
<th>${_("link options")}</th>
<td class="prefright">
%if c.user_is_loggedin and c.user.jury_betatester():
%if c.user_is_loggedin:
<p>
${checkbox(_("show the spotlight box on the front page"), "organic")}
&#32;

View File

@@ -33,11 +33,6 @@
${ynbutton(_("delete"), _("deleted"), "del", "hide_thing")}
</li>
%endif
%if thing.show_indict:
<li>
${ynbutton(_("indict"), _("indicted"), "indict")}
</li>
%endif
%if thing.can_ban:
%if not getattr(thing.thing, "use_big_modbuttons", False):
%if not thing.show_spam:
@@ -240,24 +235,6 @@
%endif
%endif
%if getattr(thing.thing, "reveal_trial_info", False):
<li class="rounded trial-stamp stamp"
title='${_('questionable submission; you should remove it or approve it')}'
>
${_("deputy opinion:")}
&#32;
<span class="spam">
${_("%d spam") % thing.thing.trial_info.get("spams", "?")}
</span>
&#32;
/
&#32;
<span class="kosher">
${_("%d kosher") % thing.thing.trial_info.get("koshers", "?")}
</span>
</li>
%endif
%if thing.show_reports and not thing.show_spam:
<li class="rounded reported-stamp stamp">
${strings.reports % thing.thing.reported}