diff --git a/r2/r2/config/routing.py b/r2/r2/config/routing.py
index 0cdbc5725..8f0f0956b 100644
--- a/r2/r2/config/routing.py
+++ b/r2/r2/config/routing.py
@@ -318,7 +318,7 @@ def make_map():
requirements=dict(action="login|reg"))
mc('/post/:action', controller='post',
requirements=dict(action="options|over18|unlogged_options|optout"
- "|optin|login|reg"))
+ "|optin|login|reg|explore_settings"))
mc('/api', controller='redirect', action='redirect', dest='/dev/api')
mc('/api/distinguish/:how', controller='api', action="distinguish")
diff --git a/r2/r2/controllers/front.py b/r2/r2/controllers/front.py
index 07e45bf50..54d92cdb8 100755
--- a/r2/r2/controllers/front.py
+++ b/r2/r2/controllers/front.py
@@ -32,6 +32,7 @@ from r2.controllers.reddit_base import (
)
from r2 import config
from r2.models import *
+from r2.models.recommend import ExploreSettings
from r2.config.extensions import is_api
from r2.lib import recommender
from r2.lib.pages import *
@@ -155,22 +156,12 @@ class FrontController(RedditController):
kw['reverse'] = False
return DetailsPage(thing=thing, expand_children=False, **kw).render()
- @validate(VUser(),
- personalized=VBoolean('pers', default=False),
- discovery=VBoolean('disc', default=False),
- rising=VBoolean('ris', default=False),
- over18=VBoolean('over18', default=False))
- def GET_explore(self, personalized, discovery, rising, over18):
- # default when no params are given is to show everything
- if not any([personalized, discovery, rising]):
- personalized = discovery = rising = True
- settings = recommender.ExploreSettings(personalized=personalized,
- discovery=discovery,
- rising=rising,
- over18=over18)
+ @validate(VUser())
+ def GET_explore(self):
+ settings = ExploreSettings.for_user(c.user)
recs = recommender.get_recommended_content_for_user(c.user,
- record_views=True,
- settings=settings)
+ settings,
+ record_views=True)
content = ExploreItemListing(recs, settings)
return BoringPage(_("explore"),
show_sidebar=True,
diff --git a/r2/r2/controllers/post.py b/r2/r2/controllers/post.py
index 72a81ebba..3a51bb70a 100644
--- a/r2/r2/controllers/post.py
+++ b/r2/r2/controllers/post.py
@@ -26,7 +26,9 @@ from api import ApiController
from r2.lib.utils import Storage, query_string, UrlParser
from r2.lib.emailer import opt_in, opt_out
from r2.lib.validator import *
+from r2.models.recommend import ExploreSettings
from pylons import request, c, g
+from pylons.controllers.util import redirect_to
from pylons.i18n import _
from r2.models import *
import hashlib
@@ -204,3 +206,26 @@ class PostController(ApiController):
def GET_login(self, *a, **kw):
return self.redirect('/login' + query_string(dict(dest="/")))
+ @validatedForm(
+ VUser(),
+ VModhash(),
+ personalized=VBoolean('pers', default=False),
+ discovery=VBoolean('disc', default=False),
+ rising=VBoolean('ris', default=False),
+ nsfw=VBoolean('nsfw', default=False),
+ )
+ def POST_explore_settings(self,
+ form,
+ jquery,
+ personalized,
+ discovery,
+ rising,
+ nsfw):
+ ExploreSettings.record_settings(
+ c.user,
+ personalized=personalized,
+ discovery=discovery,
+ rising=rising,
+ nsfw=nsfw,
+ )
+ return redirect_to(controller='front', action='explore')
diff --git a/r2/r2/lib/recommender.py b/r2/r2/lib/recommender.py
index 0891356b0..2de7c1c8e 100644
--- a/r2/r2/lib/recommender.py
+++ b/r2/r2/lib/recommender.py
@@ -36,7 +36,11 @@ from r2.lib.sgm import sgm
from r2.models import Account, Link, Subreddit
from r2.models.builder import CommentBuilder
from r2.models.listing import NestedListing
-from r2.models.recommend import AccountSRPrefs, AccountSRFeedback
+from r2.models.recommend import (
+ AccountSRPrefs,
+ AccountSRFeedback,
+ ExploreSettings,
+)
from pylons import g
from pylons.i18n import _
@@ -94,9 +98,9 @@ def get_recommendations(srs,
def get_recommended_content_for_user(account,
+ settings,
record_views=False,
- src=SRC_EXPLORE,
- settings=None):
+ src=SRC_EXPLORE):
"""Wrapper around get_recommended_content() that fills in user info.
If record_views == True, the srs will be noted in the user's preferences
@@ -109,7 +113,6 @@ def get_recommended_content_for_user(account,
"""
prefs = AccountSRPrefs.for_user(account)
- settings = settings or ExploreSettings()
recs = get_recommended_content(prefs, src, settings)
if record_views:
# mark as seen so they won't be shown again too soon
@@ -155,7 +158,7 @@ def get_recommended_content(prefs, src, settings):
to_omit=omit_srid36s,
source=src,
match_set=False,
- over18=settings.over18)
+ over18=settings.nsfw)
random.shuffle(recommended_srs)
# split list of recommended srs in half
midpoint = len(recommended_srs) / 2
@@ -182,7 +185,7 @@ def get_recommended_content(prefs, src, settings):
seen_srs = set()
recs = []
for r in all_recs:
- if not settings.over18 and r.is_over18():
+ if not settings.nsfw and r.is_over18():
continue
if not is_visible(r.sr): # could happen in rising items
continue
@@ -260,19 +263,6 @@ def is_visible(sr):
return sr.type != 'private' and not sr._spam
-class ExploreSettings(object):
- """Controls what kinds of content will be included."""
- def __init__(self,
- personalized=True,
- discovery=True,
- rising=True,
- over18=False):
- self.personalized = personalized
- self.discovery = discovery
- self.rising = rising
- self.over18 = over18
-
-
class SRRecommendation(tdb_cassandra.View):
_use_db = True
diff --git a/r2/r2/models/recommend.py b/r2/r2/models/recommend.py
index d470c9239..bdcd614be 100644
--- a/r2/r2/models/recommend.py
+++ b/r2/r2/models/recommend.py
@@ -127,3 +127,51 @@ class AccountSRFeedback(tdb_cassandra.DenormalizedRelation):
@classmethod
def record_views(cls, account, srs):
cls.record_feedback(account, srs, VIEW)
+
+
+class ExploreSettings(tdb_cassandra.Thing):
+ """Column family for storing users' view prefs for the /explore page."""
+ _use_db = True
+ _bool_props = ('personalized', 'discovery', 'rising', 'nsfw')
+
+ @classmethod
+ def for_user(cls, account):
+ """Return user's prefs or default prefs if user has none."""
+ try:
+ return cls._byID(account._id36)
+ except tdb_cassandra.NotFound:
+ return DefaultExploreSettings()
+
+ @classmethod
+ def record_settings(cls,
+ user,
+ personalized=False,
+ discovery=False,
+ rising=False,
+ nsfw=False):
+ """Update or create settings for user."""
+ try:
+ settings = cls._byID(user._id36)
+ except tdb_cassandra.NotFound:
+ settings = ExploreSettings(
+ _id=user._id36,
+ personalized=personalized,
+ discovery=discovery,
+ rising=rising,
+ nsfw=nsfw,
+ )
+ else:
+ settings.personalized = personalized
+ settings.discovery = discovery
+ settings.rising = rising
+ settings.nsfw = nsfw
+ settings._commit()
+
+
+class DefaultExploreSettings(object):
+ """Default values to use when no settings have been saved for the user."""
+ def __init__(self):
+ self.personalized = True
+ self.discovery = True
+ self.rising = True
+ self.nsfw = False
diff --git a/r2/r2/templates/exploreitemlisting.html b/r2/r2/templates/exploreitemlisting.html
index d22dcb35a..1c631c86b 100644
--- a/r2/r2/templates/exploreitemlisting.html
+++ b/r2/r2/templates/exploreitemlisting.html
@@ -32,7 +32,8 @@
${_("feedback/suggestions")}
-