From ff002a4bce7a79f4746f5ba3235580816ef85e51 Mon Sep 17 00:00:00 2001 From: David King Date: Thu, 24 Oct 2013 11:57:48 -0700 Subject: [PATCH] Add backend for new gold feature: "remember my visits". Store and mark what links users have already read, even between computers. --- r2/example.ini | 2 -- r2/r2/controllers/api.py | 9 ++++++ r2/r2/controllers/post.py | 1 + r2/r2/lib/jsontemplates.py | 2 +- r2/r2/lib/template_helpers.py | 10 ++++++- r2/r2/models/account.py | 1 + r2/r2/models/link.py | 51 +++++++++++++++++++------------- r2/r2/templates/prefoptions.html | 3 ++ 8 files changed, 55 insertions(+), 24 deletions(-) diff --git a/r2/example.ini b/r2/example.ini index dadea2ab6..e0c9a4597 100644 --- a/r2/example.ini +++ b/r2/example.ini @@ -248,7 +248,6 @@ db_table_link = thing db_table_account = thing db_table_message = thing db_table_savehide = relation, account, link -db_table_click = relation, account, link db_table_comment = thing db_table_subreddit = thing db_table_srmember = relation, subreddit, account @@ -277,7 +276,6 @@ db_servers_link = main, main db_servers_account = main db_servers_message = main db_servers_savehide = main -db_servers_click = main db_servers_comment = comment db_servers_subreddit = comment db_servers_srmember = comment diff --git a/r2/r2/controllers/api.py b/r2/r2/controllers/api.py index 06f175563..9dc41c98c 100755 --- a/r2/r2/controllers/api.py +++ b/r2/r2/controllers/api.py @@ -3632,3 +3632,12 @@ class ApiController(RedditController, OAuth2ResourceController): def POST_server_seconds_visibility(self, form, jquery, seconds_visibility): c.user.pref_public_server_seconds = seconds_visibility == "public" c.user._commit() + + @noresponse(VGold(), + links = VByName('links', thing_cls=Link, multiple=True, + limit=100)) + def POST_store_visits(self, links): + if not c.user.pref_store_visits or not links: + return + + LinkVisitsByAccount._visit(c.user, links) diff --git a/r2/r2/controllers/post.py b/r2/r2/controllers/post.py index 085a51fc8..72a81ebba 100644 --- a/r2/r2/controllers/post.py +++ b/r2/r2/controllers/post.py @@ -99,6 +99,7 @@ class PostController(ApiController): pref_collapse_read_messages = VBoolean("collapse_read_messages"), pref_private_feeds = VBoolean("private_feeds"), pref_local_js = VBoolean('local_js'), + pref_store_visits = VBoolean('store_visits'), pref_show_adbox = VBoolean("show_adbox"), pref_show_sponsors = VBoolean("show_sponsors"), pref_show_sponsorships = VBoolean("show_sponsorships"), diff --git a/r2/r2/lib/jsontemplates.py b/r2/r2/lib/jsontemplates.py index 6329d27c5..bdfb52417 100755 --- a/r2/r2/lib/jsontemplates.py +++ b/r2/r2/lib/jsontemplates.py @@ -391,7 +391,7 @@ class LinkJsonTemplate(ThingJsonTemplate): author_flair_css_class="author_flair_css_class", author_flair_text="author_flair_text", banned_by="banned_by", - clicked="clicked", + visited="visited", distinguished="distinguished", domain="domain", downs="downvotes", diff --git a/r2/r2/lib/template_helpers.py b/r2/r2/lib/template_helpers.py index 11a172bff..e3a3a550e 100755 --- a/r2/r2/lib/template_helpers.py +++ b/r2/r2/lib/template_helpers.py @@ -116,9 +116,12 @@ def media_https_if_secure(url): def js_config(extra_config=None): + logged = c.user_is_loggedin and c.user.name + gold = bool(logged and c.user.gold) + config = { # is the user logged in? - "logged": c.user_is_loggedin and c.user.name, + "logged": logged, # the subreddit's name (for posts) "post_site": c.site.name if not c.default_sr else "", # are we in an iframe? @@ -127,6 +130,11 @@ def js_config(extra_config=None): "modhash": c.modhash or False, # the current rendering style "renderstyle": c.render_style, + + # they're welcome to try to override this in the DOM because we just + # disable the features server-side if applicable + 'store_visits': gold and c.user.pref_store_visits, + # current domain "cur_domain": get_domain(cname=c.frameless_cname, subreddit=False, no_www=True), # where do ajax requests go? diff --git a/r2/r2/models/account.py b/r2/r2/models/account.py index d741ef9a4..492bf6fe1 100644 --- a/r2/r2/models/account.py +++ b/r2/r2/models/account.py @@ -63,6 +63,7 @@ class Account(Thing): pref_frame_commentspanel = False, pref_newwindow = False, pref_clickgadget = 5, + pref_store_visits = False, pref_public_votes = False, pref_hide_from_robots = False, pref_research = False, diff --git a/r2/r2/models/link.py b/r2/r2/models/link.py index 1a957509c..77404eaf1 100755 --- a/r2/r2/models/link.py +++ b/r2/r2/models/link.py @@ -206,13 +206,6 @@ class Link(Thing, Printable): LinkSavesByAccount._unsave(user, self) return self._unsomething(user, self._saved, 'save') - @classmethod - def _clicked(cls, user, link): - return cls._somethinged(Click, user, link, 'click') - - def _click(self, user): - return self._something(Click, user, self._clicked, 'click') - @classmethod def _hidden(cls, user, link): return cls._somethinged(SaveHide, user, link, 'hide') @@ -374,6 +367,9 @@ class Link(Thing, Printable): pref_newwindow = user.pref_newwindow cname = c.cname site = c.site + now = datetime.now(g.tz) + + saved = hidden = visited = {} if user_is_admin: # Checking if a domain's banned isn't even cheap @@ -388,13 +384,14 @@ class Link(Thing, Printable): try: saved = LinkSavesByAccount.fast_query(user, wrapped) hidden = LinkHidesByAccount.fast_query(user, wrapped) - except tdb_cassandra.TRANSIENT_EXCEPTIONS as e: - g.log.warning("Cassandra save/hide lookup failed: %r", e) - saved = hidden = {} - clicked = {} - else: - saved = hidden = clicked = {} + if user.gold and user.pref_store_visits: + visited = LinkVisitsByAccount.fast_query(user, wrapped) + + except tdb_cassandra.TRANSIENT_EXCEPTIONS as e: + # saved or hidden or may have been done properly, so go ahead + # with what we do have + g.log.warning("Cassandra save/hide/visited lookup failed: %r", e) for item in wrapped: show_media = False @@ -460,10 +457,10 @@ class Link(Thing, Printable): if user_is_loggedin: item.saved = (user, item) in saved item.hidden = (user, item) in hidden + item.visited = (user, item) in visited - item.clicked = bool(clicked.get((user, item, 'click'))) else: - item.saved = item.hidden = item.clicked = False + item.saved = item.hidden = item.visited = False item.num = None item.permalink = item.make_permalink(item.subreddit) @@ -569,7 +566,7 @@ class Link(Thing, Printable): item.fresh = not any((item.likes != None, item.saved, - item.clicked, + item.visited, item.hidden, item._deleted, item._spam)) @@ -586,7 +583,8 @@ class Link(Thing, Printable): item.author = DeletedUser() item.as_deleted = True - item_age = datetime.now(g.tz) - item._date + item_age = now - item._date + if item_age.days > g.VOTE_AGE_LIMIT and item.promoted is None: item.votable = False else: @@ -977,6 +975,7 @@ class Comment(Thing, Printable): focal_comment = c.focal_comment cname = c.cname site = c.site + now = datetime.now(g.tz) if user_is_loggedin: gilded = [comment for comment in wrapped if comment.gildings > 0] @@ -1031,7 +1030,7 @@ class Comment(Thing, Printable): item.can_reply = False if c.can_reply or (item.sr_id in can_reply_srs): - age = datetime.now(g.tz) - item._date + age = now - item._date if item.link.promoted or age.days < g.REPLY_AGE_LIMIT: item.can_reply = True @@ -1534,8 +1533,6 @@ class Message(Thing, Printable): return True class SaveHide(Relation(Account, Link)): pass -class Click(Relation(Account, Link)): pass - class GildedCommentsByAccount(tdb_cassandra.DenormalizedRelation): _use_db = True @@ -1709,6 +1706,20 @@ class LinkHidesByAccount(_ThingHidesByAccount): from r2.lib.db import queries return [queries.get_hidden_links(user)] +class LinkVisitsByAccount(_SaveHideByAccount): + _use_db = True + _last_modified_name = 'Visit' + _views = [] + _ttl = timedelta(days=7) + _write_consistency_level = tdb_cassandra.CL.ONE + + @classmethod + def _visit(cls, user, things): + cls._savehide(user, things) + + @classmethod + def _unvisit(cls, user, things): + cls._unsavehide(user, things) class _ThingSavesBySubreddit(tdb_cassandra.View): @classmethod diff --git a/r2/r2/templates/prefoptions.html b/r2/r2/templates/prefoptions.html index 61f16e396..9bc5dddb8 100644 --- a/r2/r2/templates/prefoptions.html +++ b/r2/r2/templates/prefoptions.html @@ -316,6 +316,9 @@ ${checkbox(_("show sponsorships"), "show_sponsorships")} (${_("the 300x100 'sponsored by...' images that sometimes appear in sidebars")})
+ ${checkbox(_("remember what links I've visited"), "store_visits")} + (${_("we'll remember and mark what links you've already read, even between computers")}) +

${checkbox(_("highlight new comments"), "highlight_new_comments")}