From d3b58e7cc9fd60b823c83e0c4d43f57a7ab93ef5 Mon Sep 17 00:00:00 2001 From: Samuel Clay Date: Fri, 20 Jul 2012 19:43:28 -0700 Subject: [PATCH] Deferring OPML import if it takes > 20 seconds. Email user when complete. Also, hamburgers. This one's for @torrez. --- apps/feed_import/models.py | 35 +++++++++- apps/feed_import/tasks.py | 21 ++++++ apps/feed_import/views.py | 26 ++++++-- apps/profile/models.py | 17 +++++ fabfile.py | 2 +- media/css/reader.css | 62 +++++++++++------- media/img/reader/hamburger_l.png | Bin 0 -> 2327 bytes media/img/reader/hamburger_m.png | Bin 0 -> 1911 bytes media/img/reader/hamburger_s.png | Bin 0 -> 1495 bytes media/js/newsblur/reader/reader.js | 11 ++-- .../js/newsblur/reader/reader_feedchooser.js | 23 +++++-- media/js/newsblur/reader/reader_intro.js | 24 +++++-- settings.py | 5 +- templates/mail/email_base.txt | 8 ++- templates/mail/email_base.xhtml | 24 +++---- templates/mail/email_upload_opml_finished.txt | 7 ++ .../mail/email_upload_opml_finished.xhtml | 15 +++++ utils/feed_functions.py | 25 ++++--- 18 files changed, 231 insertions(+), 74 deletions(-) create mode 100644 apps/feed_import/tasks.py create mode 100644 media/img/reader/hamburger_l.png create mode 100644 media/img/reader/hamburger_m.png create mode 100644 media/img/reader/hamburger_s.png create mode 100644 templates/mail/email_upload_opml_finished.txt create mode 100644 templates/mail/email_upload_opml_finished.xhtml diff --git a/apps/feed_import/models.py b/apps/feed_import/models.py index e144ca136..5164f17b8 100644 --- a/apps/feed_import/models.py +++ b/apps/feed_import/models.py @@ -1,5 +1,6 @@ import datetime import oauth2 as oauth +import mongoengine as mongo from collections import defaultdict from StringIO import StringIO from xml.etree.ElementTree import Element, SubElement, Comment, tostring @@ -13,7 +14,7 @@ from apps.rss_feeds.models import Feed, DuplicateFeed, MStarredStory from apps.reader.models import UserSubscription, UserSubscriptionFolders from utils import json_functions as json, urlnorm from utils import log as logging - +from utils.feed_functions import timelimit class OAuthToken(models.Model): user = models.OneToOneField(User, null=True, blank=True) @@ -96,12 +97,17 @@ class OPMLImporter(Importer): def __init__(self, opml_xml, user): self.user = user self.opml_xml = opml_xml - + + def try_processing(self): + folders = timelimit(20)(self.process)() + return folders + def process(self): - outline = opml.from_string(self.opml_xml) self.clear_feeds() + outline = opml.from_string(self.opml_xml) folders = self.process_outline(outline) UserSubscriptionFolders.objects.create(user=self.user, folders=json.encode(folders)) + return folders def process_outline(self, outline): @@ -169,7 +175,30 @@ class OPMLImporter(Importer): us.save() folders.append(feed_db.pk) return folders + + def count_feeds_in_opml(self): + opml_count = len(opml.from_string(self.opml_xml)) + sub_count = UserSubscription.objects.filter(user=self.user).count() + return opml_count + sub_count + + +class UploadedOPML(mongo.Document): + user_id = mongo.IntField() + opml_file = mongo.StringField() + upload_date = mongo.DateTimeField(default=datetime.datetime.now) + + def __unicode__(self): + user = User.objects.get(pk=self.user_id) + return "%s: %s characters" % (user.username, len(self.opml_file)) + + meta = { + 'collection': 'uploaded_opml', + 'allow_inheritance': False, + 'order': '-upload_date', + 'indexes': ['user_id', '-upload_date'], + } + class GoogleReaderImporter(Importer): diff --git a/apps/feed_import/tasks.py b/apps/feed_import/tasks.py new file mode 100644 index 000000000..d0f6c1c7c --- /dev/null +++ b/apps/feed_import/tasks.py @@ -0,0 +1,21 @@ +from celery.task import Task +from django.contrib.auth.models import User +from apps.feed_import.models import UploadedOPML, OPMLImporter +from apps.reader.models import UserSubscription +from utils import log as logging + + +class ProcessOPML(Task): + + def run(self, user_id): + user = User.objects.get(pk=user_id) + logging.user(user, "~FR~SBOPML upload (task) starting...") + + opml = UploadedOPML.objects.filter(user_id=user_id).first() + opml_importer = OPMLImporter(opml.opml_file, user) + opml_importer.process() + + feed_count = UserSubscription.objects.filter(user=user).count() + user.profile.send_upload_opml_finished_email(feed_count) + logging.user(user, "~FR~SBOPML upload (task): ~SK%s~SN~SB~FR feeds" % (feed_count)) + diff --git a/apps/feed_import/views.py b/apps/feed_import/views.py index ba8095135..659decd0b 100644 --- a/apps/feed_import/views.py +++ b/apps/feed_import/views.py @@ -14,9 +14,12 @@ from django.contrib.auth import login as login_user from django.shortcuts import render_to_response from apps.reader.forms import SignupForm from apps.reader.models import UserSubscription -from apps.feed_import.models import OAuthToken, OPMLImporter, OPMLExporter, GoogleReaderImporter +from apps.feed_import.models import OAuthToken, GoogleReaderImporter +from apps.feed_import.models import OPMLImporter, OPMLExporter, UploadedOPML +from apps.feed_import.tasks import ProcessOPML from utils import json_functions as json from utils.user_functions import ajax_login_required, get_user +from utils.feed_functions import TimeoutError @ajax_login_required @@ -31,11 +34,24 @@ def opml_upload(request): logging.user(request, "~FR~SBOPML upload starting...") file = request.FILES['file'] xml_opml = file.read() + UploadedOPML.objects.create(user_id=request.user.pk, opml_file=xml_opml) + opml_importer = OPMLImporter(xml_opml, request.user) - folders = opml_importer.process() - feeds = UserSubscription.objects.filter(user=request.user).values() - payload = dict(folders=folders, feeds=feeds) - logging.user(request, "~FR~SBOPML Upload: ~SK%s~SN~SB~FR feeds" % (len(feeds))) + try: + folders = opml_importer.try_processing() + except TimeoutError: + folders = None + ProcessOPML.delay(request.user.pk) + feed_count = opml_importer.count_feeds_in_opml() + logging.user(request, "~FR~SBOPML pload took too long, found %s feeds. Tasking..." % feed_count) + payload = dict(folders=folders, delayed=True, feed_count=feed_count) + code = 2 + message = "" + + if folders: + feeds = UserSubscription.objects.filter(user=request.user).values() + payload = dict(folders=folders, feeds=feeds) + logging.user(request, "~FR~SBOPML Upload: ~SK%s~SN~SB~FR feeds" % (len(feeds))) request.session['import_from_google_reader'] = False else: diff --git a/apps/profile/models.py b/apps/profile/models.py index d2453b149..7ec2c4c91 100644 --- a/apps/profile/models.py +++ b/apps/profile/models.py @@ -214,6 +214,23 @@ NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()} logging.user(self.user, "~BB~FM~SBSending email for social beta: %s" % self.user.email) + def send_upload_opml_finished_email(self, feed_count): + if not self.user.email: + print "Please provide an email address." + return + + user = self.user + text = render_to_string('mail/email_upload_opml_finished.txt', locals()) + html = render_to_string('mail/email_upload_opml_finished.xhtml', locals()) + subject = "Your OPML upload is complete. Get going with NewsBlur!" + msg = EmailMultiAlternatives(subject, text, + from_email='NewsBlur <%s>' % settings.HELLO_EMAIL, + to=['%s <%s>' % (user, user.email)]) + msg.attach_alternative(html, "text/html") + msg.send() + + logging.user(self.user, "~BB~FM~SBSending email for OPML upload: %s" % self.user.email) + def autologin_url(self, next=None): return reverse('autologin', kwargs={ 'username': self.user.username, diff --git a/fabfile.py b/fabfile.py index e7e103aad..b8fda3a51 100644 --- a/fabfile.py +++ b/fabfile.py @@ -374,7 +374,7 @@ def setup_psycopg(): def setup_python(): # sudo('easy_install -U pip') - sudo('easy_install -U fabric django==1.3.1 readline pyflakes iconv celery django-celery django-celery-with-redis django-compress South django-extensions pymongo==2.2.0 stripe BeautifulSoup pyyaml nltk lxml oauth2 pytz boto seacucumber django_ses mongoengine redis requests') + sudo('easy_install -U fabric django==1.3.1 readline pyflakes iconv celery django-celery django-celery-with-redis django-compress South django-extensions pymongo==2.2.0 stripe BeautifulSoup pyyaml nltk lxml oauth2 pytz boto seacucumber django_ses mongoengine redis requests psutil') put('config/pystartup.py', '.pystartup') # with cd(os.path.join(env.NEWSBLUR_PATH, 'vendor/cjson')): diff --git a/media/css/reader.css b/media/css/reader.css index b22580162..cbf5959fd 100644 --- a/media/css/reader.css +++ b/media/css/reader.css @@ -5872,6 +5872,13 @@ form.opml_import_form input { min-height: 234px; } +.NB-modal-intro .NB-intro-import-delayed { + color: #808080; + margin: 12px 0 0; + font-size: 12px; + text-align: center; + display: none; +} .NB-modal-intro .NB-intro-import { width: 300px; float: left; @@ -6337,16 +6344,15 @@ form.opml_import_form input { } .NB-modal-feedchooser .NB-modal-subtitle { - font-weight: normal; - font-size: 12px; - color: #606060; - text-shadow: 1px 1px 0 #F0F0F0; - width: 715px; + font-weight: normal; + font-size: 12px; + color: #606060; + text-shadow: 1px 1px 0 #F0F0F0; + width: 715px; } .NB-modal-feedchooser .NB-modal-subtitle b { -/* margin: 0 0 4px 0;*/ padding-right: 8px; - color: #303030; + color: #303030; } .NB-modal-feedchooser .NB-feedchooser-subtitle-type-prefix { @@ -6354,19 +6360,33 @@ form.opml_import_form input { } .NB-modal-feedchooser .NB-feedchooser-type { - float: left; - width: 345px; - margin: 0 12px 0 0; - padding: 0 12px 0 0; + float: left; + width: 345px; + margin: 0 52px 0 26px; + padding: 0; } .NB-modal-feedchooser .NB-feedchooser-type.NB-last { - margin: 0 0 0 0; - padding: 0 0 0 24px; - margin-right: 0; - border-left: 1px solid #B0B0B0; + margin: 0 0 0 0; + padding: 0 0 0 52px; + margin-right: 0; + border-left: 1px solid #B0B0B0; } +.NB-modal-feedchooser .NB-feedchooser-porpoise { + border-radius: 16px; + line-height: 48px; + color: #808080; + font-size: 18px; + padding: 8px 2px; + background-color: white; + position: absolute; + margin-top: -24px; + width: 30px; + text-align: center; + top: 50%; + left: -17px +} .NB-modal-feedchooser .NB-feedchooser-info { overflow: hidden; } @@ -6510,22 +6530,20 @@ form.opml_import_form input { position: absolute; width: 40px; height: 40px; - top: 0; + top: -10px; left: -40px; + display: none; } .NB-modal-feedchooser .NB-feedchooser-dollar-value.NB-selected.NB-1 .NB-feedchooser-dollar-image { - top: -8px; - background: transparent url('/media/embed/reader/hamburgers.png') no-repeat 0 0; + display: block; } .NB-modal-feedchooser .NB-feedchooser-dollar-value.NB-selected.NB-2 .NB-feedchooser-dollar-image { - top: -8px; - background: transparent url('/media/embed/reader/hamburgers.png') no-repeat 0 -36px; + display: block; } .NB-modal-feedchooser .NB-feedchooser-dollar-value.NB-selected.NB-3 .NB-feedchooser-dollar-image { - top: -10px; - background: transparent url('/media/embed/reader/hamburgers.png') no-repeat 0 -72px; + display: block; } .NB-modal-feedchooser .NB-feedchooser-dollar-month { diff --git a/media/img/reader/hamburger_l.png b/media/img/reader/hamburger_l.png new file mode 100644 index 0000000000000000000000000000000000000000..c0f28f38525f0ce54dbc6b6bf85f4117ba329b9a GIT binary patch literal 2327 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 literal 0 HcmV?d00001 diff --git a/media/img/reader/hamburger_s.png b/media/img/reader/hamburger_s.png new file mode 100644 index 0000000000000000000000000000000000000000..aa612eb860f05123455d057942d96697659e797d GIT binary patch literal 1495 zcmV;|1t|K7P)$I z4$uv(NQ;nAB}5BCNCok+Xi+O6iVz#fg3?WqN~IDO?XC+xBt%ugf&d~_OG|~CRBdR3 z-86Q3cimI0B4|I(H2=AQpg_uPBV5mn_{lKFoFyj^=hy|f3^ zOM5`Qbj<+;S2rTE<3n!#k=VHA4XDgGm)%TIoVLcs&TC>cpA*s3*!WM{x%5|a_@QJc zF|QVA`R8T_K>3+(PNLnB`_L;1KhGi5%)e{5-T;4$>Xh_z0QPn>qVxKaH zk6-++0(DM(>RuDX4-)n5lemUvUBnhJM({30Oo$OBS3zuqsM|c~Gf0~Y-lq>o@Bh)G z|1prh{(x=2@rOsSh5N*Y5X4AOMr;US1&qZ2Di%z*8OP_~I(P{X%^|&vx*VUke;D67 zd%x7a>vj#a{?x>m1WpJbL4*WF#6*ZOXi&tI#?d^*SOaMjEsY|+3t57?95h3{!{=RG zp5fB$%R_ga{qe4VUjIR5pcuK|g(klx2*B6?W24R65sL_j7%&0Y5HS%5paw)ymu=pR z%gD2wZYQOk^72N@eRTH0%k`~*f`6m<7p<-*)w54J|SzK^pf;TL~iBVSmh zKUFRV8SSHEoY^Jv+(W$8KZpEicdfe2%q7i{hAN{7B4m1T0MUK`aDOgQI1`VJK%Q;X8Tu zOxa^!xc>1)XTCDm4spYVH?9lWsD{iR5NKU?B}sVCL{(l|>Kd&#sFnhz4p#Zlu(CFv zbM|CFr4AohOqpGFgyNt4?DH4y*;UF`zKFHc{3N+(?_7DMKx;7~ON!WrCW6Q$0Ezrxy znX)0T7Aq9P0;mIRgKt_2D?*Z3noejrsC5J_LfWtN)jeVCVMOtR9$!#$pwot=0cn>H zf8_f-zZKB7Z)kXsrACv-+jZ{D90xp`T_PwsI#Xa=P6Ze+-~zxw$$>u%nL?I=Pa#i0 zJrqJV!ZOdcEsO8uxufx8;Za5#ZEovk%&KxE47ovsN%i!LZ1&YF-2w!RfI! page_count) { NEWSBLUR.assets.preference('has_setup_feeds', true); NEWSBLUR.reader.check_hide_getting_started(); - this.close(function() { - NEWSBLUR.reader.open_dialog_after_feeds_loaded(); - }); + this.close(_.bind(function() { + NEWSBLUR.reader.open_dialog_after_feeds_loaded({delayed_import: this.flags.delayed_import}); + }, this)); return; } else if (page_number == 1) { $('.NB-tutorial-next-page-text', this.$modal).text("Let's Get Started "); @@ -345,6 +351,7 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, { if (NEWSBLUR.assets.feeds.size() && !this.options.force_import) { page = 2; $('.NB-intro-imports-sites', this.$modal).addClass('active'); + $('.NB-intro-import-delayed', this.$modal).hide(); } else { page = 0; $('.NB-intro-imports-start', this.$modal).addClass('active'); @@ -360,11 +367,12 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, { this.count_feeds(); }, - count_feeds: function() { - var feed_count = NEWSBLUR.assets.feeds.size(); + count_feeds: function(fake_feed_count) { + var feed_count = fake_feed_count || NEWSBLUR.assets.feeds.size(); $(".NB-intro-imports-sites h4", this.$modal).text([ 'You are subscribed to ', + (fake_feed_count && 'at least '), Inflector.pluralize(' site', feed_count, true), '.' ].join("")); @@ -464,10 +472,16 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, { var params = { url: NEWSBLUR.URLs['opml-upload'], type: 'POST', + dataType: 'json', success: function (data, status) { NEWSBLUR.assets.load_feeds(function() { $loading.removeClass('NB-active'); self.advance_import_carousel(2); + if (data.payload.delayed) { + NEWSBLUR.reader.flags.delayed_import = true; + self.count_feeds(data.payload.feed_count); + $('.NB-intro-import-delayed', self.$modal).show(); + } }); NEWSBLUR.reader.load_recommended_feed(); }, diff --git a/settings.py b/settings.py index 378747e50..d47e5df5e 100644 --- a/settings.py +++ b/settings.py @@ -310,7 +310,10 @@ BROKER_URL = "redis://db01:6379/0" CELERY_REDIS_HOST = "db01" CELERYD_PREFETCH_MULTIPLIER = 1 -CELERY_IMPORTS = ("apps.rss_feeds.tasks", "apps.social.tasks", "apps.reader.tasks",) +CELERY_IMPORTS = ("apps.rss_feeds.tasks", + "apps.social.tasks", + "apps.reader.tasks", + "apps.feed_import.tasks",) CELERYD_CONCURRENCY = 4 CELERY_IGNORE_RESULT = True CELERY_ACKS_LATE = True # Retry if task fails diff --git a/templates/mail/email_base.txt b/templates/mail/email_base.txt index b5a340a52..26e73ba43 100644 --- a/templates/mail/email_base.txt +++ b/templates/mail/email_base.txt @@ -1,3 +1,5 @@ +{% load utils_tags %} + {% block body %}{% endblock body %} - Samuel & Roy @@ -18,10 +20,10 @@ Stay up to date and in touch with us, yr. developers, in a few different ways: There's plenty of ways to use NewsBlur beyond the website: - * Download the free iPhone App: http://www.newsblur.com/iphone/ + * Download the free iPhone App: http://{% current_domain %}/iphone/ * Download the Android App on the Android Market: https://market.android.com/details?id=bitwrit.Blar - * Download browser extensions for Safari, Firefox, and Chrome: http://www.newsblur.com{{ user.profile.autologin_url }}?next=goodies + * Download browser extensions for Safari, Firefox, and Chrome: http://{% current_domain %}{{ user.profile.autologin_url }}?next=goodies ----------------------------------------------------------------------------- -Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur: http://www.newsblur.com{{ user.profile.autologin_url }}?next=optout \ No newline at end of file +Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur: http://{% current_domain %}{{ user.profile.autologin_url }}?next=optout \ No newline at end of file diff --git a/templates/mail/email_base.xhtml b/templates/mail/email_base.xhtml index d395e97e9..5f1c7d0e9 100644 --- a/templates/mail/email_base.xhtml +++ b/templates/mail/email_base.xhtml @@ -1,3 +1,5 @@ +{% load utils_tags %} + @@ -12,11 +14,11 @@ - @@ -62,7 +64,7 @@ @@ -71,7 +73,7 @@
- NewsBlur + NewsBlur
+ {% block body %}{% endblock %} @@ -32,23 +34,23 @@

Stay up to date and in touch with us, yr. developers, in a few different ways:

{% block resources_header %}To get the most out of NewsBlur, here are a few resources:{% endblock resources_header %}

There's plenty of ways to use NewsBlur beyond the web:

- Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur. + Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur.

- NewsBlur + NewsBlur
diff --git a/templates/mail/email_upload_opml_finished.txt b/templates/mail/email_upload_opml_finished.txt new file mode 100644 index 000000000..7383d4824 --- /dev/null +++ b/templates/mail/email_upload_opml_finished.txt @@ -0,0 +1,7 @@ +{% extends "mail/email_base.txt" %} + +{% load utils_tags %} + +{% block body %}Good news! NewsBlur has finished importing your OPML file. You are now subscribed to {{ feed_count }} site{{ feed_count|pluralize }}. + +Head over to NewsBlur and get reading: http://{% current_domain %}{% endblock body %} \ No newline at end of file diff --git a/templates/mail/email_upload_opml_finished.xhtml b/templates/mail/email_upload_opml_finished.xhtml new file mode 100644 index 000000000..a8fe8a646 --- /dev/null +++ b/templates/mail/email_upload_opml_finished.xhtml @@ -0,0 +1,15 @@ +{% extends "mail/email_base.xhtml" %} + +{% load utils_tags %} + +{% block body %} + +

+ Good news! NewsBlur has finished importing your OPML file. You are now + subscribed to {{ feed_count }} site{{ feed_count|pluralize }}. +

+ +

+ Head over to NewsBlur and get reading. +

+{% endblock %} diff --git a/utils/feed_functions.py b/utils/feed_functions.py index 468185824..4b59516cb 100644 --- a/utils/feed_functions.py +++ b/utils/feed_functions.py @@ -27,19 +27,18 @@ def timelimit(timeout): self.result = function(*args, **kw) except: self.error = sys.exc_info() - if not settings.DEBUG and not settings.TEST_DEBUG: - c = Dispatch() - c.join(timeout) - if c.isAlive(): - raise TimeoutError, 'took too long' - if c.error: - tb = ''.join(traceback.format_exception(c.error[0], c.error[1], c.error[2])) - logging.debug(tb) - mail_admins('Error in timeout: %s' % c.error[0], tb) - raise c.error[0], c.error[1] - return c.result - else: - return function(*args, **kw) + c = Dispatch() + c.join(timeout) + if c.isAlive(): + raise TimeoutError, 'took too long' + if not settings.DEBUG and not settings.TEST_DEBUG and c.error: + tb = ''.join(traceback.format_exception(c.error[0], c.error[1], c.error[2])) + logging.debug(tb) + mail_admins('Error in timeout: %s' % c.error[0], tb) + raise c.error[0], c.error[1] + return c.result + # else: + # return function(*args, **kw) return _2 return _1