From 1180100e379cda58ca891b289c39ab11a38e3071 Mon Sep 17 00:00:00 2001 From: Samuel Clay Date: Sun, 24 Jan 2010 22:53:46 -0500 Subject: [PATCH] - Fixing caching of feeds and users. - Added loading indicator on new feed. - Cleaned up numerous bugs around showing/hiding out-of-score stories. --- .gitignore | 1 + apps/analyzer/models.py | 2 +- apps/reader/urls.py | 5 ++- apps/reader/views.py | 67 +++++++++++---------------------- media/js/newsblur/assetmodel.js | 12 ++++-- media/js/newsblur/reader.js | 57 +++++++++++++++++----------- settings.py | 4 +- templates/reader/feeds.xhtml | 4 +- urls.py | 4 +- utils/feed_fetcher.py | 2 + 10 files changed, 77 insertions(+), 81 deletions(-) diff --git a/.gitignore b/.gitignore index d9c671af9..821f62a61 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ logs/*.log +*.pyc \ No newline at end of file diff --git a/apps/analyzer/models.py b/apps/analyzer/models.py index 1c779c844..d4e66b21a 100644 --- a/apps/analyzer/models.py +++ b/apps/analyzer/models.py @@ -83,7 +83,7 @@ class ClassifierTag(models.Model): def apply_classifier_titles(classifiers, story): for classifier in classifiers: - if classifier.title in story['story_title']: + if classifier.title.lower() in story['story_title'].lower(): # print 'Titles: (%s) %s -- %s' % (classifier.title in story['story_title'], classifier.title, story['story_title']) return classifier.score return 0 diff --git a/apps/reader/urls.py b/apps/reader/urls.py index b586c3825..01a3839da 100644 --- a/apps/reader/urls.py +++ b/apps/reader/urls.py @@ -2,11 +2,12 @@ from django.conf.urls.defaults import * from apps.reader import views urlpatterns = patterns('', - url(r'^$', views.index, name='index'), + url(r'^$', views.index), + url(r'^logout', views.logout, name='logout'), + url(r'^login', views.login, name='login'), (r'^load_single_feed', views.load_single_feed), (r'^load_feed_page', views.load_feed_page), (r'^load_feeds', views.load_feeds), - (r'^refresh_feed', views.refresh_feed), (r'^mark_story_as_read', views.mark_story_as_read), (r'^mark_story_as_like', views.mark_story_as_like), (r'^mark_story_as_dislike', views.mark_story_as_dislike), diff --git a/apps/reader/views.py b/apps/reader/views.py index f240c4beb..8765f0a0f 100644 --- a/apps/reader/views.py +++ b/apps/reader/views.py @@ -7,17 +7,18 @@ try: except: pass from django.core.cache import cache +from django.views.decorators.cache import never_cache from django.db.models.aggregates import Count from apps.reader.models import UserSubscription, UserSubscriptionFolders, UserStory from utils import json from utils.user_functions import get_user +from django.core.urlresolvers import reverse from django.contrib.auth.models import User from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth import authenticate, login from django.http import HttpResponse, HttpRequest, HttpResponseRedirect from django.core import serializers from django.utils.safestring import mark_safe -from django.views.decorators.cache import cache_page from djangologging.decorators import suppress_logging_output from apps.analyzer.models import ClassifierFeed, ClassifierAuthor, ClassifierTag, ClassifierTitle from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags @@ -28,29 +29,32 @@ import random SINGLE_DAY = 60*60*24 +@never_cache def index(request): - # feeds = Feed.objects.filter(usersubscription__user=request.user) - # for f in feeds: - # f.update() - - # context = feeds - context = {} - print request.user + print "User: %s" % request.user form = AuthenticationForm(request.POST) - user = request.user - user_info = _parse_user_info(user) return render_to_response('reader/feeds.xhtml', {'form': form}, context_instance=RequestContext(request)) +@never_cache def login(request): - username = request.POST['username'] - password = request.POST['password'] - user = authenticate(username=username, password=password) - if user is not None: - if user.is_active: - login(request, user) - return HttpResponseRedirect(reverse('index')) + if request.method == "POST": + form = AuthenticationForm(data=request.POST) + if form.is_valid(): + from django.contrib.auth import login + login(request, form.get_user()) + return HttpResponseRedirect(reverse('index')) + + return index(request) +@never_cache +def logout(request): + print "Logout: %s" % request.user + from django.contrib.auth import logout + logout(request) + + return HttpResponseRedirect(reverse('index')) + def load_feeds(request): user = get_user(request) @@ -158,35 +162,6 @@ def load_single_feed(request): data = json.encode(context) return HttpResponse(data, mimetype='application/json') -def refresh_feed(request): - feed_id = request.REQUEST['feed_id'] - force_update = request.GET.get('force', False) - feeds = Feed.objects.filter(id=feed_id) - - feeds = refresh_feeds(feeds, force_update) - - context = {} - - user = request.user - user_info = _parse_user_info(user) - context.update(user_info) - - return render_to_response('reader/feeds.xhtml', context, - context_instance=RequestContext(request)) - -def refresh_feeds(feeds, force=False): - for f in feeds: - logging.debug('Feed Updating: %s' % f) - f.update(force) - usersubs = UserSubscription.objects.filter( - feed=f.id - ) - for us in usersubs: - us.count_unread() - logging.info('Deleteing user sub cache: %s' % us.user_id) - cache.delete('usersub:%s' % us.user_id) - return - @suppress_logging_output def load_feed_page(request): feed = Feed.objects.get(id=request.REQUEST.get('feed_id')) diff --git a/media/js/newsblur/assetmodel.js b/media/js/newsblur/assetmodel.js index bf7852e09..2ffe2267d 100644 --- a/media/js/newsblur/assetmodel.js +++ b/media/js/newsblur/assetmodel.js @@ -34,7 +34,7 @@ NEWSBLUR.AssetModel.Reader.prototype = { return; }, - make_request: function(url, data, callback) { + make_request: function(url, data, callback, error_callback) { $.ajax({ url: url, data: data, @@ -71,6 +71,7 @@ NEWSBLUR.AssetModel.Reader.prototype = { }, error: function(e) { NEWSBLUR.log(['AJAX Error', e]); + error_callback(); } }); }, @@ -87,13 +88,15 @@ NEWSBLUR.AssetModel.Reader.prototype = { } } - if (!read) { + if (!read && NEWSBLUR.Globals.is_authenticated) { this.make_request('/reader/mark_story_as_read', { story_id: story_id, feed_id: feed_id }, callback ); + } else { + callback(read); } }, @@ -161,7 +164,7 @@ NEWSBLUR.AssetModel.Reader.prototype = { this.make_request('/reader/load_feeds', {}, pre_callback); }, - load_feed: function(feed_id, page, first_load, callback) { + load_feed: function(feed_id, page, first_load, callback, error_callback) { var self = this; var pre_callback = function(data) { @@ -182,7 +185,8 @@ NEWSBLUR.AssetModel.Reader.prototype = { { feed_id: feed_id, page: page - }, pre_callback + }, pre_callback, + error_callback ); }, diff --git a/media/js/newsblur/reader.js b/media/js/newsblur/reader.js index dbd712fac..a111bbe1d 100644 --- a/media/js/newsblur/reader.js +++ b/media/js/newsblur/reader.js @@ -48,6 +48,7 @@ // ================= make_story_title: function(story) { + var unread_view = NEWSBLUR.Globals.unread_view; var read = story.read_status ? 'read' : ''; @@ -75,6 +76,10 @@ $.make('div', { className: 'NB-story-sentiment NB-story-dislike' }) ]).data('story_id', story.id); + if (unread_view > score) { + $story_title.css({'display': 'none'}); + } + return $story_title; }, @@ -411,6 +416,7 @@ this.show_feed_title_in_stories($story_titles, feed_id); this.mark_feed_as_selected(feed_id, $feed_link); + this.show_feedbar_loading(); this.model.load_feed(feed_id, 0, true, $.rescope(this.post_open_feed, this)); // this.model.load_feed_page(feed_id, 0, $.rescope(this.show_feed_page_contents, this)); this.show_feed_page_contents(feed_id); @@ -631,22 +637,35 @@ }, load_page_of_feed_stories: function() { + var $feedbar; var $story_titles = this.$story_titles; var feed_id = $story_titles.data('feed_id'); var page = $story_titles.data('page'); - var $feedbar = $.make('div', { className: 'NB-story-titles-end-stories-line' }); - $feedbar.css({'background': '#E1EBFF'}); - $story_titles.append($feedbar); - $feedbar.animate({'backgroundColor': '#5C89C9'}, {'duration': 750}) - .animate({'backgroundColor': '#E1EBFF'}, 750); - this.feed_stories_loading = setInterval(function() { - $feedbar.animate({'backgroundColor': '#5C89C9'}, {'duration': 750}) - .animate({'backgroundColor': '#E1EBFF'}, 750); - }, 1500); + this.show_feedbar_loading(); $story_titles.data('page', page+1); - this.model.load_feed(feed_id, page+1, false, $.rescope(this.post_open_feed, this)); + this.model.load_feed(feed_id, page+1, false, + $.rescope(this.post_open_feed, this), + $.rescope(this.load_page_of_feed_stories, this)); + }, + + show_feedbar_loading: function() { + var $story_titles = this.$story_titles; + var $feedbar = $('.NB-story-titles-end-stories-line'); + + if (!$feedbar.length) { + $feedbar = $.make('div', { className: 'NB-story-titles-end-stories-line' }); + } + $feedbar.css({'background': '#E1EBFF'}); + $story_titles.append($feedbar); + + $feedbar.animate({'backgroundColor': '#5C89C9'}, {'duration': 750}) + .animate({'backgroundColor': '#E1EBFF'}, 750); + this.feed_stories_loading = setInterval(function() { + $feedbar.animate({'backgroundColor': '#5C89C9'}, {'duration': 750}) + .animate({'backgroundColor': '#E1EBFF'}, 750); + }, 1500); }, hover_over_story_titles: function() { @@ -881,7 +900,7 @@ if (story) { var self = this; var $story = this.find_story_in_story_frame(story); - // NEWSBLUR.log(['Prefetching story', s, story, $story]); + NEWSBLUR.log(['Prefetching story', s, story, $story]); setTimeout(function() { // NEWSBLUR.log(['Fetching next story', s]); @@ -1070,16 +1089,16 @@ var $feed_list = this.$feed_list; var $feed = $('.feed.selected', $feed_list); - var callback = function() { + var callback = function(read) { + if (read) return; + var unread_count_positive = parseInt($('.unread_count_positive', $feed).text(), 10); var unread_count_neutral = parseInt($('.unread_count_neutral', $feed).text(), 10); var unread_count_negative = parseInt($('.unread_count_negative', $feed).text(), 10); - NEWSBLUR.log(['marked read', unread_count_positive, unread_count_neutral, unread_count_negative, $story_title.is('.NB-story-positive'), $story_title.is('.NB-story-neutral'), $story_title.is('.NB-story-negative')]); + // NEWSBLUR.log(['marked read', unread_count_positive, unread_count_neutral, unread_count_negative, $story_title.is('.NB-story-positive'), $story_title.is('.NB-story-neutral'), $story_title.is('.NB-story-negative')]); if ($story_title.is('.NB-story-positive')) { $('.unread_count_positive', $feed).text(unread_count_positive-1); - $('.unread_count_neutral', $feed).text(unread_count_neutral-1); - $('.unread_count_negative', $feed).text(unread_count_negative-1); if (unread_count_positive == 1) { $feed.removeClass('unread_positive'); } else { @@ -1087,9 +1106,7 @@ } } else if ($story_title.is('.NB-story-neutral')) { $('.unread_count_neutral', $feed).text(unread_count_neutral-1); - $('.unread_count_negative', $feed).text(unread_count_negative-1); if (unread_count_neutral == 1) { - $feed.removeClass('unread_positive'); $feed.removeClass('unread_neutral'); } else { $feed.addClass('unread_neutral'); @@ -1097,8 +1114,6 @@ } else if ($story_title.is('.NB-story-negative')) { $('.unread_count_negative', $feed).text(unread_count_negative-1); if (unread_count_negative == 1) { - $feed.removeClass('unread_positive'); - $feed.removeClass('unread_neutral'); $feed.removeClass('unread_negative'); } else { $feed.addClass('unread_negative'); @@ -1110,9 +1125,7 @@ $story_title.addClass('read'); - if (NEWSBLUR.Globals.is_authenticated) { - this.model.mark_story_as_read(story_id, feed_id, callback); - } + this.model.mark_story_as_read(story_id, feed_id, callback); }, mark_feed_as_read: function(feed_id) { diff --git a/settings.py b/settings.py index 8a2cfa5c0..d3f123779 100644 --- a/settings.py +++ b/settings.py @@ -104,7 +104,7 @@ elif DEV_SERVER1: # Example: "/Users/media/media.lawrence.com/" MEDIA_URL = '/media/' DEBUG = True - CACHE_BACKEND = 'dummy:///' + CACHE_BACKEND = 'locmem:///' logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', filename=LOG_FILE, @@ -121,7 +121,7 @@ elif DEV_SERVER2: # Example: "/Users/media/media.lawrence.com/" MEDIA_URL = '/media/' DEBUG = True - CACHE_BACKEND = 'dummy:///' + CACHE_BACKEND = 'locmem:///' # CACHE_BACKEND = 'locmem:///' logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', diff --git a/templates/reader/feeds.xhtml b/templates/reader/feeds.xhtml index da6080d4c..b8af61850 100644 --- a/templates/reader/feeds.xhtml +++ b/templates/reader/feeds.xhtml @@ -28,7 +28,7 @@ {% if user.is_authenticated %}
Welcome, {{ user.username}}. - (Logout) + (Logout)
{% endif %} {% if not user.is_authenticated %} @@ -39,7 +39,7 @@
Want an account?
Talk to @samuelclay.
-
+
{{ form.username.label_tag }} {{ form.username }} diff --git a/urls.py b/urls.py index 0153bc8c1..6ec550a92 100644 --- a/urls.py +++ b/urls.py @@ -1,12 +1,12 @@ from django.conf.urls.defaults import * from django.conf import settings - +from apps.reader import views as reader_views # Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', - (r'^$', include('apps.reader.urls')), + url(r'^$', reader_views.index, name='index'), (r'^accounts/', include('apps.registration.urls')), (r'^reader/', include('apps.reader.urls')), (r'^classifier/', include('apps.analyzer.urls')), diff --git a/utils/feed_fetcher.py b/utils/feed_fetcher.py index ac9eff3f0..92cdd2b70 100644 --- a/utils/feed_fetcher.py +++ b/utils/feed_fetcher.py @@ -259,6 +259,8 @@ class Dispatcher: if ENTRY_NEW in ret_entries and ret_entries[ENTRY_NEW]: user_subs = UserSubscription.objects.filter(feed=feed) for sub in user_subs: + logging.info('Deleteing user sub cache: %s' % sub.user_id) + cache.delete('usersub:%s' % sub.user_id) sub.calculate_feed_scores() except: (etype, eobj, etb) = sys.exc_info()