From aae5369e6dab510705fdfa1265cf9e095d24a97d Mon Sep 17 00:00:00 2001
From: Radek Czajka
Date: Thu, 22 Mar 2012 15:56:18 +0100
Subject: [PATCH] celery waiter for custompdf
---
apps/catalogue/forms.py | 68 +++++++-------
apps/catalogue/utils.py | 2 +-
apps/catalogue/views.py | 4 +-
apps/waiter/__init__.py | 5 +-
apps/waiter/locale/pl/LC_MESSAGES/django.mo | Bin 0 -> 1858 bytes
apps/waiter/locale/pl/LC_MESSAGES/django.po | 67 ++++++++++++++
apps/waiter/models.py | 29 ++++--
apps/waiter/templates/waiter/wait.html | 93 +++++++++++++++++---
apps/waiter/views.py | 15 +++-
lib/librarian | 2 +-
wolnelektury/settings/celery.py | 1 +
wolnelektury/settings/custom.py | 2 +
12 files changed, 223 insertions(+), 65 deletions(-)
create mode 100644 apps/waiter/locale/pl/LC_MESSAGES/django.mo
create mode 100644 apps/waiter/locale/pl/LC_MESSAGES/django.po
diff --git a/apps/catalogue/forms.py b/apps/catalogue/forms.py
index 75d9ab997..d191d46db 100644
--- a/apps/catalogue/forms.py
+++ b/apps/catalogue/forms.py
@@ -35,54 +35,46 @@ class DownloadFormatsForm(forms.Form):
widget=forms.CheckboxSelectMultiple)
def __init__(self, *args, **kwargs):
- super(DownloadFormatsForm, self).__init__(*args, **kwargs)
+ super(DownloadFormatsForm, self).__init__(*args, **kwargs)
-PDF_PAGE_SIZES = (
- ('a4paper', _('A4')),
- ('a5paper', _('A5')),
-)
-
-
-PDF_LEADINGS = (
- ('', _('Normal leading')),
- ('onehalfleading', _('One and a half leading')),
- ('doubleleading', _('Double leading')),
+CUSTOMIZATION_FLAGS = (
+ ('nofootnotes', _("Don't show footnotes")),
+ ('nothemes', _("Don't disply themes")),
+ ('nowlfont', _("Don't use our custom font")),
)
-
-PDF_FONT_SIZES = (
- ('11pt', _('Default')),
- ('13pt', _('Big'))
+CUSTOMIZATION_OPTIONS = (
+ ('leading', _("Leading"), (
+ ('defaultleading', _('Normal leading')),
+ ('onehalfleading', _('One and a half leading')),
+ ('doubleleading', _('Double leading')),
+ )),
+ ('fontsize', _("Font size"), (
+ ('11pt', _('Default')),
+ ('13pt', _('Big'))
+ )),
+# ('pagesize', _("Paper size"), (
+# ('a4paper', _('A4')),
+# ('a5paper', _('A5')),
+# )),
)
class CustomPDFForm(forms.Form):
- nofootnotes = forms.BooleanField(required=False, label=_("Don't show footnotes"))
- nothemes = forms.BooleanField(required=False, label=_("Don't disply themes"))
- nowlfont = forms.BooleanField(required=False, label=_("Don't use our custom font"))
- ## pagesize = forms.ChoiceField(PDF_PAGE_SIZES, required=True, label=_("Paper size"))
- leading = forms.ChoiceField(PDF_LEADINGS, required=False, label=_("Leading"))
- fontsize = forms.ChoiceField(PDF_FONT_SIZES, required=True, label=_("Font size"))
+ def __init__(self, *args, **kwargs):
+ super(CustomPDFForm, self).__init__(*args, **kwargs)
+ for name, label in CUSTOMIZATION_FLAGS:
+ self.fields[name] = forms.BooleanField(required=False, label=label)
+ for name, label, choices in CUSTOMIZATION_OPTIONS:
+ self.fields[name] = forms.ChoiceField(choices, label=label)
@property
def customizations(self):
c = []
- if self.cleaned_data['nofootnotes']:
- c.append('nofootnotes')
-
- if self.cleaned_data['nothemes']:
- c.append('nothemes')
-
- if self.cleaned_data['nowlfont']:
- c.append('nowlfont')
-
- ## c.append(self.cleaned_data['pagesize'])
- c.append(self.cleaned_data['fontsize'])
-
- if self.cleaned_data['leading']:
- c.append(self.cleaned_data['leading'])
-
+ for name, label in CUSTOMIZATION_FLAGS:
+ if self.cleaned_data.get(name):
+ c.append(name)
+ for name, label, choices in CUSTOMIZATION_OPTIONS:
+ c.append(self.cleaned_data[name])
c.sort()
-
return c
-
diff --git a/apps/catalogue/utils.py b/apps/catalogue/utils.py
index 185f5fa34..949ac96b2 100644
--- a/apps/catalogue/utils.py
+++ b/apps/catalogue/utils.py
@@ -140,7 +140,7 @@ class AttachmentHttpResponse(HttpResponse):
for chunk in read_chunks(f):
self.write(chunk)
-@task
+@task(rate_limit=settings.CATALOGUE_CUSTOMPDF_RATE_LIMIT)
def async_build_pdf(book_id, customizations, file_name):
"""
A celery task to generate pdf files.
diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py
index c8549c26f..0c05d17a8 100644
--- a/apps/catalogue/views.py
+++ b/apps/catalogue/views.py
@@ -22,7 +22,7 @@ from ajaxable.utils import JSONResponse, AjaxableFormView
from catalogue import models
from catalogue import forms
-from catalogue.utils import (split_tags, AttachmentHttpResponse,
+from catalogue.utils import (split_tags,
async_build_pdf, MultiQuerySet)
from pdcounter import models as pdcounter_models
from pdcounter import views as pdcounter_views
@@ -543,7 +543,7 @@ def download_custom_pdf(request, slug, method='GET'):
url = WaitedFile.order(pdf_file,
lambda p: async_build_pdf.delay(book.id, cust, p),
- "%s: %s" % (book.pretty_title(), ", ".join(cust))
+ book.pretty_title()
)
return redirect(url)
else:
diff --git a/apps/waiter/__init__.py b/apps/waiter/__init__.py
index 5aabbd62d..d3696b741 100644
--- a/apps/waiter/__init__.py
+++ b/apps/waiter/__init__.py
@@ -1,5 +1,8 @@
"""
-Waiting for and serving files generated in Celery to the user.
+Celery waiter.
+
+Takes orders for files generated by async Celery tasks.
+Serves the file when ready. Kindly asks the user to wait if not.
Author: Radek Czajka
"""
\ No newline at end of file
diff --git a/apps/waiter/locale/pl/LC_MESSAGES/django.mo b/apps/waiter/locale/pl/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..8f8afb7de4215c3bd40da47af73d10f49d5e88b6
GIT binary patch
literal 1858
zcmb7EO>Y}T7+#=!$lQ>61M#M)O`>KucG^mnYn!$qtpaHqrEU=j3BBGOZ^pZxS!Q;-
zwdtu+R0&QTI8@~qATB7ksuvVnf#8|>e(f5h&;;&~^5e*)hJo_y8w&H^t0mw^oY4)`tb7s$N#n&&;i
z|36N6-p|0VU$6E3H*kml>%b3y3_Jte13E%K0io^zss%G1OjPSl{5rlc<-tw8IyN_2
z#l5S7xMU;Roz>?jbX_Q6IF~A>d{@v=rh-CNlyd@MDtB=ZH0b%-P6b;*13?*dgvwV)
z8yaYxjM*fxAT4fUXsU=pJ%%WU38kD16LeOyo_8CE5Q94_#Mn}ou5{6_>9J5r8ONU8D-9=l(v9S3fg(+LxUE~qp=RqnKY1fEh_tfbn
zyKGPOKru_5CTJA4v&uc@cM^YLa0V?G@&8fM!Hm~=4j#Q9;_ko
zRRJ0zqLOW!I%3>Ay%a6O6TSlPv%|m$#s
RgxXxcp7Ba=Y-ND4WBo
zbcYncMOd|%lg-&65`{7jMTvZeT19r{h2Btzl!iu#I(mRB9};+&BG(LG7AAF>ilh-?
zPN`IhOH<%&8$H5_zZv;=g|SkrHf>*7|K!T`4Z6Frb9?jF&Bl(%w8{M&HkOfpwTP|X
z*KKNS-|GA82qK4){}FnlO>528n%_L_4uf68sK&HcV%
zWzlKcv}uPUkwHdyOqtae?;r`+~=9
z+PZS{`W+WfXKU+nDxKJo4C1)4d1K=!_*T$ttZS8{SN#4YLr}T6pRZQ^b&0}VHdf@_
zJN;|^#UnY#c_@s(p+X&@{%yKAka=S}EexmrHEqUd8~+`Z%$e%5(>?D#m)vo)(`wR#2i3WgX)Z&j1)a6!hIcp>?v!{v{~QUrWF_&;
zQ<)Sr*7Ij*4^#5&5k>Qd#r&C_&h`*gDbv~Gpt0@pned20DGYQE}
z(T;fCz(~`SlljA{L=w;HU>{bqgZ=qacpTMZ;?qf-&h{cE#puoza{fa-e?pnI)A>V9
z^QRb8nT}9WThEG$F;l-pU&gv-9yD%euszawO~&jLGn{rujeT20BOD
zsS8DMH8uio{Wv?9I!sW!66DW$%i%TG<;1CEdVsP^UhqDzIcYVi=pxdWXT&iJZX&uU
zCm0R}c|bC&w9g)4ER_fQquR, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-03-22 15:54+0100\n"
+"PO-Revision-Date: 2012-03-22 15:54+0100\n"
+"Last-Translator: Radek Czajka \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: templates/waiter/wait.html:7
+#: templates/waiter/wait.html.py:33
+msgid "The file is ready for download!"
+msgstr "Plik jest gotowy do pobrania!"
+
+#: templates/waiter/wait.html:10
+#: templates/waiter/wait.html.py:42
+msgid "Your file is being prepared, please wait."
+msgstr "Plik jest generowany, proszÄ czekaÄ."
+
+#: templates/waiter/wait.html:12
+#: templates/waiter/wait.html.py:51
+msgid "Something went wrong."
+msgstr "CoÅ poszÅo nie tak."
+
+#: templates/waiter/wait.html:36
+#, python-format
+msgid ""
+"Your file is ready!\n"
+" If the download doesn't start in a few seconds,\n"
+" feel free to use this direct link."
+msgstr ""
+"Twój plik jest gotowy!\n"
+"JeÅli pobieranie nie zacznie siÄ w ciÄ gu kilku sekund,\n"
+"skorzystaj z tego bezpoÅredniego linku."
+
+#: templates/waiter/wait.html:45
+#, python-format
+msgid "The file you requested was: %(d)s."
+msgstr "Zamówiony plik to: %(d)s."
+
+#: templates/waiter/wait.html:47
+msgid ""
+"Be aware: Generating the file can take a while.\n"
+" Please be patient, or bookmark this page and come back later.
"
+msgstr ""
+"Uwaga: Generowanie pliku może trwaÄ dÅuższÄ chwilÄ.\n"
+"Poczekaj cierpliwie, albo dodaj tÄ stronÄ do zakÅadek i wrÃ³Ä później.
"
+
+#: templates/waiter/wait.html:55
+#, python-format
+msgid ""
+"Something seems to have gone wrong while generating your file.\n"
+" Please order it again or complain to us about it."
+msgstr ""
+"WyglÄ da na to, że coÅ poszÅo źle podczas generowania Twojego pliku.\n"
+"Spróbuj zamówiÄ go jeszcze raz albo napisz do nas."
+
diff --git a/apps/waiter/models.py b/apps/waiter/models.py
index fd9730131..26a9a6d4d 100644
--- a/apps/waiter/models.py
+++ b/apps/waiter/models.py
@@ -1,7 +1,9 @@
from os.path import join, abspath, exists
+from django.core.urlresolvers import reverse
from django.db import models
from waiter.settings import WAITER_ROOT, WAITER_URL
-from django.core.urlresolvers import reverse
+from djcelery.models import TaskMeta
+
class WaitedFile(models.Model):
path = models.CharField(max_length=255, unique=True, db_index=True)
@@ -23,31 +25,44 @@ class WaitedFile(models.Model):
Won't open a path leading outside of WAITER_ROOT.
"""
abs_path = cls.abspath(path)
- # Pre-fetch objects to avoid minor race condition
+ # Pre-fetch objects for deletion to avoid minor race condition
relevant = [o.id for o in cls.objects.filter(path=path)]
- print abs_path
if exists(abs_path):
cls.objects.filter(id__in=relevant).delete()
return True
else:
return False
+ def is_stale(self):
+ if self.task is None:
+ # Race; just let the other task roll.
+ return False
+ try:
+ meta = TaskMeta.objects.get(task_id=self.task)
+ assert meta.status in (u'PENDING', u'STARTED', u'SUCCESS', u'RETRY')
+ except TaskMeta.DoesNotExist:
+ # Might happen it's not yet there.
+ pass
+ except AssertionError:
+ return True
+ return False
+
@classmethod
def order(cls, path, task_creator, description=None):
"""
Returns an URL for the user to follow.
If the file is ready, returns download URL.
If not, starts preparing it and returns waiting URL.
+
+ task_creator: function taking a path and generating the file;
+ description: a string or string proxy with a description for user;
"""
already = cls.exists(path)
if not already:
waited, created = cls.objects.get_or_create(path=path)
- if created:
- # TODO: makedirs
+ if created or waited.is_stale():
waited.task = task_creator(cls.abspath(path))
- print waited.task
waited.description = description
waited.save()
- # TODO: it the task exists, if stale delete, send some mail and restart
return reverse("waiter", args=[path])
return join(WAITER_URL, path)
diff --git a/apps/waiter/templates/waiter/wait.html b/apps/waiter/templates/waiter/wait.html
index 7c5a88428..f4dedc77a 100644
--- a/apps/waiter/templates/waiter/wait.html
+++ b/apps/waiter/templates/waiter/wait.html
@@ -1,21 +1,92 @@
{% extends "base.html" %}
+{% load i18n %}
+{% load url from future %}
-{% block titleextra %}ProsiÄ ciekaÄ{% endblock %}
+{% block titleextra %}
+{% if file_url %}
+ {% trans "The file is ready for download!" %}
+{% else %}
+ {% if waiting %}
+ {% trans "Your file is being prepared, please wait." %}
+ {% else %}
+ {% trans "Something went wrong." %}
+ {% endif %}
+{% endif %}
+{% endblock %}
{% block extrahead %}
- {# TODO: Not like that! What are you, caveman?! #}
-
+{% if file_url %}
+
+{% else %}
+ {% if waiting %}
+
+ {% endif %}
+{% endif %}
{% endblock %}
{% block body %}
- {% if file_url %}
-
Plik jest gotowy! JeÅli pobieranie nie zacznie siÄ automatycznie,
- oto bezpoÅredni link.
- {% else %}
-
Idź na kawÄ. To trochÄ potrwa.
-
-
{{ waiting_for.description }}
- {% endif %}
+{% if file_url %}
+
{% trans "The file is ready for download!" %}
+
+
+
{% blocktrans %}Your file is ready!
+ If the download doesn't start in a few seconds,
+ feel free to use this direct link.{% endblocktrans %}
+
+{% else %}
+ {% if waiting %}
+
{% trans "Your file is being prepared, please wait." %}
+
+
+
{% blocktrans with d=waiting.description %}The file you requested was: {{d}}.{% endblocktrans %}
+
+
{% blocktrans %}Be aware: Generating the file can take a while.
+ Please be patient, or bookmark this page and come back later.
{% endblocktrans %}
+
+ {% else %}
+
{% trans "Something went wrong." %}
+
+
+ {% url 'suggest' as s %}
+
{% blocktrans %}Something seems to have gone wrong while generating your file.
+ Please order it again or complain to us about it.{% endblocktrans %}