Email user their OPML file when it takes >15s to export. Thanks to readerman_nb for the nudge.

This commit is contained in:
Samuel Clay 2025-06-08 15:17:28 -07:00
parent 42a3bdbc02
commit c0dbc04cb7
3 changed files with 135 additions and 12 deletions

View file

@ -24,3 +24,13 @@ def ProcessOPML(user_id):
UserSubscription.queue_new_feeds(user)
UserSubscription.refresh_stale_feeds(user, exclude_new=True)
@app.task()
def ProcessOPMLExport(user_id):
user = User.objects.get(pk=user_id)
logging.user(user, "~FR~SBOPML export (task) starting...")
user.profile.send_opml_export_email(reason="Your OPML export is ready.", force=True)
logging.user(user, "~FR~SBOPML export (task) complete: sent email to %s" % user.email)

View file

@ -15,13 +15,15 @@ from django.urls import reverse
from mongoengine.errors import ValidationError
from oauth2client.client import FlowExchangeError, OAuth2WebServerFlow
from django.conf import settings
from apps.feed_import.models import OAuthToken, OPMLExporter, OPMLImporter, UploadedOPML
from apps.feed_import.tasks import ProcessOPML
from apps.feed_import.tasks import ProcessOPML, ProcessOPMLExport
from apps.reader.forms import SignupForm
from apps.reader.models import UserSubscription
from utils import json_functions as json
from utils import log as logging
from utils.feed_functions import TimeoutError
from utils.feed_functions import TimeoutError, timelimit
from utils.user_functions import ajax_login_required, get_user
@ -86,17 +88,54 @@ def opml_export(request):
now = datetime.datetime.now()
if request.GET.get("user_id") and user.is_staff:
user = User.objects.get(pk=request.GET["user_id"])
exporter = OPMLExporter(user)
opml = exporter.process()
from apps.social.models import MActivity
# Try to export OPML with a 15 second timeout (0.01s in dev for testing)
timeout_seconds = 0.01 if settings.DEBUG else 15
@timelimit(timeout_seconds)
def try_opml_export():
exporter = OPMLExporter(user)
opml = exporter.process()
return exporter, opml
MActivity.new_opml_export(user_id=user.pk, count=exporter.feed_count)
try:
exporter, opml = try_opml_export()
response = HttpResponse(opml, content_type="text/xml; charset=utf-8")
response["Content-Disposition"] = "attachment; filename=NewsBlur-%s-%s.opml" % (
user.username,
now.strftime("%Y-%m-%d"),
)
from apps.social.models import MActivity
return response
MActivity.new_opml_export(user_id=user.pk, count=exporter.feed_count)
response = HttpResponse(opml, content_type="text/xml; charset=utf-8")
response["Content-Disposition"] = "attachment; filename=NewsBlur-%s-%s.opml" % (
user.username,
now.strftime("%Y-%m-%d"),
)
return response
except TimeoutError:
# If export takes too long, queue task to email user
ProcessOPMLExport.delay(user.pk)
logging.user(user, "~FR~SBOPML export took too long, emailing...")
# Check if this is an AJAX request
if request.headers.get("X-Requested-With") == "XMLHttpRequest":
return HttpResponse(
json.encode(
{
"code": 2,
"message": "Your OPML export is being processed. You will receive an email shortly with your subscription backup.",
}
),
content_type="application/json",
)
else:
# Return HTML page for non-AJAX requests
from django.shortcuts import render
return render(
request,
"reader/opml_export_delayed.xhtml",
{
"user": user,
},
)

View file

@ -0,0 +1,74 @@
{% extends 'base.html' %}
{% load utils_tags %}
{% block extra_head_js %}
<style>
.NB-opml-export-delayed {
text-align: center;
padding: 100px 20px;
max-width: 600px;
margin: 0 auto;
}
.NB-opml-export-delayed h1 {
font-size: 36px;
color: #282F33;
margin-bottom: 20px;
font-weight: bold;
}
.NB-opml-export-delayed p {
font-size: 18px;
color: #484F53;
line-height: 1.6;
margin-bottom: 30px;
}
.NB-opml-export-delayed .NB-logo {
margin-bottom: 40px;
}
.NB-opml-export-delayed .NB-button {
display: inline-block;
padding: 12px 24px;
background-color: #3B7FC4;
color: white;
text-decoration: none;
border-radius: 4px;
font-size: 16px;
font-weight: bold;
transition: background-color 0.2s;
}
.NB-opml-export-delayed .NB-button:hover {
background-color: #2968A8;
}
</style>
{% endblock %}
{% block bodyclass %}NB-opml-export-delayed{% endblock %}
{% block content %}
<div class="NB-opml-export-delayed">
<div class="NB-logo">
<img src="{{ MEDIA_URL }}img/logo_512.png" style="height: 128px;" alt="NewsBlur" />
</div>
<h1>Your OPML export is being processed</h1>
<p>
You have a lot of subscriptions! We're preparing your OPML export file now.
</p>
<p>
<strong>You will receive an email shortly with your subscription backup attached.</strong>
</p>
<p>
The email will be sent to: <strong>{{ user.email|safe }}</strong>
</p>
<a href="/" class="NB-button">Return to NewsBlur</a>
</div>
{% endblock %}