From 619b16be3d12adf452fbffbe8a401cb8c4e109db Mon Sep 17 00:00:00 2001 From: shlurbee Date: Mon, 10 Mar 2014 14:55:16 -0700 Subject: [PATCH] Make explore page settings sticky. --- r2/r2/config/routing.py | 2 +- r2/r2/controllers/front.py | 21 ++++------- r2/r2/controllers/post.py | 25 +++++++++++++ r2/r2/lib/recommender.py | 28 +++++---------- r2/r2/models/recommend.py | 48 +++++++++++++++++++++++++ r2/r2/templates/exploreitemlisting.html | 5 +-- 6 files changed, 92 insertions(+), 37 deletions(-) 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")} -
+ + ${_("personalized")} @@ -43,7 +44,7 @@ ${_("rising")} - + ${_("nsfw")}