Completely changing how subscriptions are counted. Now happens after page load. Also retooled Google Reader import to load, count, and fetch after page load so the user is not kept waiting. Progress bars for everything.

This commit is contained in:
Samuel Clay 2010-08-17 23:40:03 -04:00
parent db3bdbc0d5
commit aa99517511
11 changed files with 216 additions and 73 deletions

View file

@ -3,8 +3,8 @@ from apps.feed_import import views
urlpatterns = patterns('apps.feed_import.views',
url(r'^opml_upload$', views.opml_upload, name='opml-upload'),
url(r'^authorize/$', views.reader_authorize, name='opml-reader-authorize'),
url(r'^callback/$', views.reader_callback, name='opml-reader-callback'),
url(r'^signup/$', views.import_signup, name='import-signup')
url(r'^authorize/$', views.reader_authorize, name='google-reader-authorize'),
url(r'^callback/$', views.reader_callback, name='google-reader-callback'),
url(r'^signup/$', views.import_signup, name='import-signup'),
url(r'^import_from_google_reader/$', views.import_from_google_reader, name='import-from-google-reader')
)

View file

@ -55,7 +55,7 @@ def reader_authorize(request):
"scope=%s&oauth_callback=http://%s%s") % (
scope,
Site.objects.get_current().domain,
reverse('opml-reader-callback'),
reverse('google-reader-callback'),
)
authorize_url = 'https://www.google.com/accounts/OAuthAuthorizeToken'
@ -131,7 +131,7 @@ def import_from_google_reader(request):
scope = "http://www.google.com/reader/api"
sub_url = "%s/0/subscription/list" % scope
code = 0
if request.user.is_authenticated():
user_tokens = OAuthToken.objects.filter(user=request.user)
if user_tokens.count():
@ -144,6 +144,7 @@ def import_from_google_reader(request):
reader_importer = GoogleReaderImporter(content, request.user)
reader_importer.process()
code = 1
del request.session['import_from_google_reader']
return dict(code=code)

View file

@ -44,7 +44,7 @@ def index(request):
feature_form = None
if request.user.is_staff:
feature_form = FeatureForm()
howitworks_page = random.randint(0, 5)
return render_to_response('reader/feeds.xhtml', {
'login_form': login_form,
@ -100,14 +100,8 @@ def load_feeds(request):
return data
user_subs = UserSubscription.objects.select_related('feed').filter(user=user)
updated_count = 0
for sub in user_subs:
if updated_count < 200 and sub.needs_unread_recalc:
# > 200 means that we counted enough, just move to refresh during live.
sub.calculate_feed_scores()
updated_count += 1
feeds[sub.feed.pk] = {
'id': sub.feed.pk,
'feed_title': sub.feed.feed_title,

View file

@ -12,6 +12,7 @@ import re
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option("-a", "--all", dest="all", action="store_true", help="All feeds [for a user, or everybody]"),
make_option("-s", "--silent", dest="silent", action="store_true", help="Inverse verbosity."),
make_option("-u", "--user", dest="user", nargs=1, help="Specify user id or username"),
make_option("-d", "--daemon", dest="daemonize", action="store_true"),
)
@ -32,7 +33,7 @@ class Command(BaseCommand):
feeds = feeds.filter(user__username=options['user'])
for f in feeds:
f.calculate_feed_scores()
f.calculate_feed_scores(silent=options['silent'])
def daemonize():
"""

View file

@ -506,7 +506,7 @@ a img {
/* = Feeds Progress Bar = */
/* ====================== */
#NB-feeds-progress {
#NB-progress {
height: 40px;
width: 100%;
position: absolute;
@ -522,11 +522,11 @@ a img {
z-index: 1;
}
.NB-feeds-progress-container {
.NB-progress-container {
border-top: 1px solid #E0E0E0;
}
#NB-feeds-progress .NB-feeds-progress-close {
#NB-progress .NB-progress-close {
width: 11px;
height: 11px;
float: right;
@ -535,19 +535,47 @@ a img {
background: transparent url('../img/icons/close.png') no-repeat 0 0;
}
#NB-feeds-progress .NB-feeds-progress-title {
#NB-progress .NB-progress-title {
padding: 5px 15px 0;
font-size: 11px;
text-transform: uppercase;
height: 14px;
}
#NB-feeds-progress .NB-feeds-progress-bar {
#NB-progress .NB-progress-bar {
height:6px;
margin:6px 50px 0;
}
#NB-feeds-progress .NB-feeds-progress-counts {
#NB-progress .NB-progress-link {
display: none;
margin: -2px 0 0;
}
#NB-progress .NB-progress-link a {
-moz-box-shadow:1px 1px 1px #202020;
-webkit-box-shadow:1px 1px 1px #202020;
box-shadow:1px 1px 1px #202020;
-moz-border-radius:4px;
border-radius: 4px;
border:1px solid #606060;
font-size:12px;
margin:0 4px 0;
padding:0px 8px;
text-decoration:none;
text-transform:uppercase;
background-color: #217412;
color: #FFF;
}
#NB-progress.NB-progress-error .NB-progress-link {
display: block;
}
#NB-progress.NB-progress-error .NB-progress-bar {
display: none;
}
#NB-progress .NB-progress-counts {
float: left;
font-size: 10px;
color: #B0B0B0;
@ -555,7 +583,7 @@ a img {
width: 50px;
}
#NB-feeds-progress .NB-feeds-progress-percentage {
#NB-progress .NB-progress-percentage {
float: right;
clear: both;
color: #B0B0B0;
@ -1923,12 +1951,13 @@ a.NB-splash-link:hover {
margin: 0 4px;
}
.NB-add .NB-opml-reader-oauth {
.NB-add .NB-google-reader-oauth {
margin: 4px 0 6px 0;
text-decoration: none;
float: left;
display: block;
}
/* ================ */
/* = Manage Feeds = */
/* ================ */

View file

@ -478,6 +478,10 @@ NEWSBLUR.AssetModel.Reader.prototype = {
}, callback, callback, {
'ajax_group': 'statistics'
});
},
start_import_from_google_reader: function(callback) {
this.make_request('/import/import_from_google_reader/', {}, callback);
}
};

View file

@ -21,7 +21,7 @@
$intelligence_slider: $('.NB-intelligence-slider'),
$mouse_indicator: $('#mouse-indicator'),
$feed_link_loader: $('#NB-feeds-list-loader'),
$feeds_progress: $('#NB-feeds-progress')
$feeds_progress: $('#NB-progress')
};
this.flags = {
'feed_view_images_loaded': {},
@ -63,7 +63,6 @@
this.start_import_from_google_reader();
} else {
this.load_feeds();
this.setup_feed_refresh();
}
this.apply_resizable_layout();
this.cornerize_buttons();
@ -538,7 +537,6 @@
this.make_feeds_folder($feed_list, folders, 0);
this.$s.$feed_link_loader.fadeOut(250);
this.check_feed_fetch_progress();
if (!folders.length) {
this.setup_ftux_add_feed_callout();
@ -550,6 +548,11 @@
$('.feed', $feed_list).tsort('.feed_title');
$('.folder', $feed_list).tsort('.folder_title');
if (NEWSBLUR.Globals.is_authenticated) {
this.start_count_unreads_after_import();
this.force_feed_refresh($.rescope(this.finish_count_unreads_after_import, this));
}
},
make_feeds_folder: function($feeds, items, depth) {
@ -669,35 +672,39 @@
var $progress = this.$s.$feeds_progress;
var percentage = parseInt(this.counts['fetched_feeds'] / (this.counts['unfetched_feeds'] + this.counts['fetched_feeds']) * 100, 10);
var titles = [
"Fetching your feeds",
"Fishing for feeds",
"Herding feeds",
"Fetching your feeds",
"Feeds are being fetched"
];
$('.NB-feeds-progress-title', $progress).text(titles[Math.floor(Math.random()*titles.length)]);
$('.NB-feeds-progress-counts-fetched', $progress).text(this.counts['fetched_feeds']);
$('.NB-feeds-progress-counts-total', $progress).text(this.counts['unfetched_feeds'] + this.counts['fetched_feeds']);
$('.NB-feeds-progress-percentage', $progress).text(percentage + '%');
$('.NB-feeds-progress-bar', $progress).progressbar({
$('.NB-progress-title', $progress).text('Fetching your feeds');
$('.NB-progress-counts', $progress).show();
$('.NB-progress-counts-fetched', $progress).text(this.counts['fetched_feeds']);
$('.NB-progress-counts-total', $progress).text(this.counts['unfetched_feeds'] + this.counts['fetched_feeds']);
$('.NB-progress-percentage', $progress).show().text(percentage + '%');
$('.NB-progress-bar', $progress).progressbar({
value: percentage
});
if (!$progress.is(':visible')) {
setTimeout(function() {
$progress.css({'display': 'block', 'opacity': 0}).animate({
'opacity': 1,
'bottom': 31
}, {
'duration': 750
});
self.$s.$feed_list.animate({'bottom': 73}, {'duration': 750});
self.show_progress_bar();
}, 1000);
}
},
show_progress_bar: function($progress) {
$progress = $progress || this.$s.$feeds_progress;
if (!$progress.is(':visible')) {
$progress.css({'display': 'block', 'opacity': 0}).animate({
'opacity': 1,
'bottom': 31
}, {
'queue': false,
'duration': 750
});
this.$s.$feed_list.animate({'bottom': 73}, {'duration': 750, 'queue': false});
}
},
hide_unfetched_feed_progress: function(permanent) {
hide_progress_bar: function(permanent) {
var $progress = this.$s.$feeds_progress;
if (permanent) {
@ -709,11 +716,20 @@
'bottom': 0
}, {
'duration': 750,
'queue': false,
'complete': function() {
$progress.css({'display': 'none'});
}
});
this.$s.$feed_list.animate({'bottom': '30px'}, {'duration': 750});
this.$s.$feed_list.animate({'bottom': '30px'}, {'duration': 750, 'queue': false});
},
hide_unfetched_feed_progress: function(permanent) {
if (permanent) {
this.model.preference('hide_fetch_progress', true);
}
this.hide_progress_bar();
},
load_sortable_feeds: function() {
@ -2655,31 +2671,126 @@
// =============================
// = Import from Google Reader =
// =============================
start_import_from_google_reader: function() {
var self = this;
var $progress = this.$s.$feeds_progress;
var $bar = $('.NB-progress-bar', $progress);
var percentage = 0;
$('.NB-progress-title', $progress).text('Importing from Google Reader');
$('.NB-progress-counts', $progress).hide();
$('.NB-progress-percentage', $progress).hide();
$bar.progressbar({
value: percentage
});
var animate = function() {
var time = 50;
if (percentage > 90) {
time = 500;
} else if (percentage > 80) {
time = 400;
} else if (percentage > 70) {
time = 300;
} else if (percentage > 60) {
time = 200;
} else if (percentage > 50) {
time = 100;
}
setTimeout(function() {
if (!self.flags['import_from_google_reader_finished']) {
percentage += 1;
$bar.progressbar({value: percentage});
animate();
}
}, time);
};
animate();
this.show_progress_bar();
this.model.start_import_from_google_reader($.rescope(this.finish_import_from_google_reader, this));
},
finish_import_from_google_reader: function(e, data) {
if (data.code >= 1) {
this.start_count_unreads_after_import();
}
var $progress = this.$s.$feeds_progress;
var $bar = $('.NB-progress-bar', $progress);
this.flags['import_from_google_reader_finished'] = true;
if (data.code >= 1) {
$bar.progressbar({value: 100});
this.load_feeds();
} else {
NEWSBLUR.log(['Import Error!', data]);
this.$s.$feed_link_loader.fadeOut(250);
$progress.addClass('NB-progress-error');
$('.NB-progress-title', $progress).text('Error importing Google Reader');
$('.NB-progress-link', $progress).html($.make('a', { href: NEWSBLUR.URLs['google-reader-authorize'], className: 'NB-splash-link' }, 'Try importing again'));
}
},
start_count_unreads_after_import: function() {
var self = this;
var $progress = this.$s.$feeds_progress;
var $bar = $('.NB-progress-bar', $progress);
var percentage = 0;
var factor = 17500 * _.keys(this.model.feeds).length / 40000;
$('.NB-progress-title', $progress).text('Counting is difficult');
$('.NB-progress-counts', $progress).hide();
$('.NB-progress-percentage', $progress).hide();
$bar.progressbar({
value: percentage
});
var animate = function() {
// 17,500 ticks
var time = factor;
if (percentage > 90) {
time = factor * 100;
} else if (percentage > 80) {
time = factor * 50;
} else if (percentage > 70) {
time = factor * 20;
} else if (percentage > 60) {
time = factor * 8;
} else if (percentage > 50) {
time = factor * 2;
}
setTimeout(function() {
if (!self.flags['count_unreads_after_import_finished']) {
percentage += 1;
$bar.progressbar({value: percentage});
animate();
}
}, time);
};
animate();
setTimeout(function() {
if (!self.flags['count_unreads_after_import_finished']) {
self.show_progress_bar();
}
}, 500);
},
finish_count_unreads_after_import: function(e, data) {
this.setup_feed_refresh();
// this.hide
this.post_feed_refresh(e, data);
$('.NB-progress-bar', this.$s.$feeds_progress).progressbar({
value: 100
});
this.flags['count_unreads_after_import_finished'] = true;
this.$s.$feed_link_loader.fadeOut(250);
this.setup_feed_refresh();
if (!this.flags['has_unfetched_feeds']) {
this.hide_progress_bar();
}
},
// ==========
// = Events =
// ==========
handle_clicks: function(elem, e) {
var self = this;
// var start = (new Date().getMilliseconds());
@ -2868,7 +2979,7 @@
e.preventDefault();
self.lock_mouse_indicator();
});
$.targetIs(e, { tagSelector: '.NB-feeds-progress-close' }, function($t, $p){
$.targetIs(e, { tagSelector: '.NB-progress-close' }, function($t, $p){
e.preventDefault();
self.hide_unfetched_feed_progress(true);
});

View file

@ -61,7 +61,7 @@ NEWSBLUR.ReaderAddFeed.prototype = {
'Import feeds'
]),
$.make('div', { className: 'NB-fieldset-fields' }, [
$.make('a', { href: NEWSBLUR.URLs['opml-reader-authorize'], className: 'NB-opml-reader-oauth NB-modal-submit-save NB-modal-submit-button' }, [
$.make('a', { href: NEWSBLUR.URLs['google-reader-authorize'], className: 'NB-google-reader-oauth NB-modal-submit-save NB-modal-submit-button' }, [
'Import from Google Reader',
$.make('img', { className: 'NB-add-google-reader-arrow', src: NEWSBLUR.Globals['MEDIA_URL']+'img/icons/silk/arrow_right.png' })
]),

View file

@ -26,7 +26,7 @@
'view_settings': {}
};
NEWSBLUR.URLs = {
'opml-reader-authorize': "{% url opml-reader-authorize %}"
'google-reader-authorize': "{% url google-reader-authorize %}"
};
</script>

View file

@ -73,7 +73,7 @@
{% endif %}
<div class="NB-signup-orline {% if signup_form.errors %}NB-signup-orline-reduced{% endif %}">&mdash; <span class="NB-signup-orline-or">or</span> &mdash;</div>
<a href="{% url opml-reader-authorize %}" class="NB-splash-link NB-signup-google">Import from<br /><img src="{{ MEDIA_URL }}img/reader/google-reader-logo.png" width="112" height="24" /></a>
<a href="{% url google-reader-authorize %}" class="NB-splash-link NB-signup-google">Import from<br /><img src="{{ MEDIA_URL }}img/reader/google-reader-logo.png" width="112" height="24" /></a>
</div>
</div>
@ -268,17 +268,18 @@
<ul class="left-center" id="feed_list"></ul>
<div id="NB-feeds-progress">
<div class="NB-feeds-progress-container">
<div class="NB-feeds-progress-close"></div>
<div class="NB-feeds-progress-title">Fetching your feeds</div>
<div class="NB-feeds-progress-percentage"></div>
<div class="NB-feeds-progress-counts">
<span class="NB-feeds-progress-counts-fetched"></span>
<div id="NB-progress">
<div class="NB-progress-container">
<div class="NB-progress-close"></div>
<div class="NB-progress-title">Fetching your feeds</div>
<div class="NB-progress-percentage"></div>
<div class="NB-progress-link"></div>
<div class="NB-progress-counts">
<span class="NB-progress-counts-fetched"></span>
/
<span class="NB-feeds-progress-counts-total"></span>
<span class="NB-progress-counts-total"></span>
</div>
<div class="NB-feeds-progress-bar"></div>
<div class="NB-progress-bar"></div>
</div>
</div>

View file

@ -264,7 +264,9 @@ class Dispatcher:
user_subs = UserSubscription.objects.filter(feed=feed)
for sub in user_subs:
cache.delete('usersub:%s' % sub.user_id)
sub.calculate_feed_scores(silent=True)
# sub.calculate_feed_scores(silent=True)
sub.needs_unread_recalc = True
sub.save()
if ret_entries.get(ENTRY_NEW) or ret_entries.get(ENTRY_UPDATED):
feed.get_stories(force=True)