From: Radek Czajka
Date: Thu, 22 Mar 2012 14:56:18 +0000 (+0100)
Subject: celery waiter for custompdf
X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/aae5369e6dab510705fdfa1265cf9e095d24a97d?ds=inline
celery waiter for custompdf
---
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 000000000..8f8afb7de
Binary files /dev/null and b/apps/waiter/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/apps/waiter/locale/pl/LC_MESSAGES/django.po b/apps/waiter/locale/pl/LC_MESSAGES/django.po
new file mode 100644
index 000000000..bbc9f8dfa
--- /dev/null
+++ b/apps/waiter/locale/pl/LC_MESSAGES/django.po
@@ -0,0 +1,67 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , 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 %}