pass message from celery up to the front end (bug #652566)

This commit is contained in:
Andy McKay 2011-04-28 17:59:35 -07:00
Родитель 9ad3e2df41
Коммит 2fab97fc64
9 изменённых файлов: 121 добавлений и 24 удалений

Просмотреть файл

@ -5,6 +5,7 @@ import os
from django.conf import settings
from django.core import mail
from django.core.cache import cache
from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils import encoding
@ -17,7 +18,7 @@ import test_utils
import amo
from amo import urlresolvers, utils, helpers
from amo.utils import ImageCheck, Token
from amo.utils import ImageCheck, Message, Token
from versions.models import License
@ -407,6 +408,32 @@ class TestToken(test_utils.TestCase):
assert not new.well_formed()
class TestMessage(test_utils.TestCase):
def test_message_save(self):
new = Message('abc')
new.save('123')
new = Message('abc')
eq_(new.get(), '123')
def test_message_expires(self):
new = Message('abc')
new.save('123')
cache.clear()
new = Message('abc')
eq_(new.get(), None)
def test_message_get_delete(self):
new = Message('abc')
new.save('123')
new = Message('abc')
eq_(new.get(delete=True), '123')
eq_(new.get(delete=True), None)
def test_site_nav():
r = Mock()
r.APP = amo.FIREFOX

Просмотреть файл

@ -449,7 +449,34 @@ def memoize(prefix, time=60):
return decorator
class Message:
"""
A simple message class for when you don't have a session, but wish
to pass a message through memcache. For example, memcache up to the
user.
"""
def __init__(self, key):
self.key = '%s:message:%s' % (settings.CACHE_PREFIX, key)
def delete(self):
cache.delete(self.key)
def save(self, message, time=60 * 5):
cache.set(self.key, message, time)
def get(self, delete=False):
res = cache.get(self.key)
cache.delete(self.key)
return res
class Token:
"""
A simple token, useful for security. It can have an expiry
or be grabbed and deleted. It will check that the key is valid and
and well formed before checking. If you don't have a key, it will
generate a randomish one for you.
"""
_well_formed = re.compile('^[a-z0-9-]+$')
def __init__(self, token=None, data=True):

Просмотреть файл

@ -41,12 +41,12 @@ def file_tree(files, selected):
t = env.get_template('files/node.html')
for k, v in files.items():
if v['depth'] > depth:
output.append('<ul class="hidden">')
output.append('<ul class="js-hidden">')
elif v['depth'] < depth:
output.extend(['</ul>' for x in range(v['depth'], depth) ])
output.extend(['</ul>' for x in range(v['depth'], depth)])
output.append(t.render(value=v, selected=selected))
depth = v['depth']
output.extend(['</ul>' for x in range(depth, -1, -1) ])
output.extend(['</ul>' for x in range(depth, -1, -1)])
return jinja2.Markup('\n'.join(output))
@ -71,10 +71,12 @@ class FileViewer:
os.makedirs(os.path.dirname(self.dest))
except OSError, err:
pass
try:
extract_xpi(self.src, self.dest, expand=True)
except Exception, err:
task_log.error('Error (%s) extracting %s' % (err, self.src))
raise
def cleanup(self):
if os.path.exists(self.dest):

Просмотреть файл

@ -1,16 +1,28 @@
from django.conf import settings
from celeryutils import task
import commonware.log
from tower import ugettext as _
from amo.utils import Message
task_log = commonware.log.getLogger('z.task')
@task
def extract_file(viewer, **kw):
msg = Message('file-viewer:%s' % viewer)
msg.delete()
task_log.info('[1@%s] Unzipping %s for file viewer.' % (
extract_file.rate_limit, viewer))
try:
viewer.extract()
except ValueError, msg:
except Exception, err:
if settings.DEBUG:
msg.save(_('There was an error accessing file %s. %s.') %
(viewer, err))
else:
msg.save(_('There was an error accessing file %s.') % viewer)
task_log.error('[1@%s] Error unzipping: %s' %
(extract_file.rate_limit, msg))
(extract_file.rate_limit, err))

Просмотреть файл

@ -18,6 +18,7 @@
<p class="waiting" id="extracting" data-url="{{ poll_url }}">
{{ _('Add-on file being processed, please wait.') }}
</p>
<div class="notification-box error js-hidden"></div>
{% endif %}
<div id="files">
{% if files %}
@ -36,7 +37,7 @@
<p>{{ _('No files in the uploaded file.') }}</p>
{% endif %}
</div>
<div id="thinking" class="hidden">
<div id="thinking" class="js-hidden">
<p class="waiting">
{{ _('Fetching file.') }}
</p>
@ -65,9 +66,9 @@
</p>
{% endif %}
{% if text_one and text_two %}
<pre id="diff" class="wrapped hidden"></pre>
<pre class="left hidden">{{ text_one }}</pre>
<pre class="right hidden">{{ text_two }}</pre>
<pre id="diff" class="wrapped js-hidden"></pre>
<pre class="left js-hidden">{{ text_one }}</pre>
<pre class="right js-hidden">{{ text_two }}</pre>
{% endif %}
{% if selected and not selected['directory']%}
{% if diff.one %}

Просмотреть файл

@ -13,6 +13,7 @@ from nose.tools import eq_
from pyquery import PyQuery as pq
import test_utils
from amo.utils import Message
from amo.urlresolvers import reverse
from addons.models import Addon
from files.helpers import FileViewer, DiffHelper
@ -319,6 +320,15 @@ class TestFileViewer(FilesBase, test_utils.TestCase):
doc = pq(res.content)
assert doc('p.notification-box').text().startswith('File size is')
def test_poll_failed(self):
msg = Message('file-viewer:%s' % self.file_viewer)
msg.save('I like cheese.')
res = self.client.get(self.poll_url())
eq_(res.status_code, 200)
data = json.loads(res.content)
eq_(data['status'], False)
eq_(data['msg'], ['I like cheese.'])
class TestDiffViewer(FilesBase, test_utils.TestCase):
fixtures = ['base/addon_3615', 'base/users']

Просмотреть файл

@ -11,7 +11,7 @@ import jingo
from access import acl
from amo.decorators import json_view
from amo.urlresolvers import reverse
from amo.utils import HttpResponseSendFile, Token
from amo.utils import HttpResponseSendFile, Message, Token
from files.decorators import file_view, compare_file_view, file_view_token
from files.tasks import extract_file
@ -43,7 +43,8 @@ def setup_viewer(request, file_obj):
@json_view
@file_view
def files_poll(request, viewer):
return {'status': viewer.is_extracted}
return {'status': viewer.is_extracted,
'msg': [Message('file-viewer:%s' % viewer).get(delete=True)]}
@file_view
@ -79,7 +80,12 @@ def files_list(request, viewer, key='install.rdf'):
@compare_file_view
@json_view
def files_compare_poll(request, diff):
return {'status': diff.is_extracted}
msgs = []
for f in (diff.file_one, diff.file_two):
m = Message('file-viewer:%s' % f).get(delete=True)
if m:
msgs.append(m)
return {'status': diff.is_extracted, 'msg': msgs}
@compare_file_view
@ -109,7 +115,8 @@ def files_compare(request, diff, key='install.rdf'):
data['msg'] = omsg or tmsg
else:
extract_file.delay(diff)
extract_file.delay(diff.file_one)
extract_file.delay(diff.file_two)
response = jingo.render(request, 'files/viewer.html', data)
if not settings.DEBUG:

Просмотреть файл

@ -37,7 +37,7 @@ span.number {
}
pre {
overflow-x: scroll;
overflow-x: auto;
}
pre.wrapped {

Просмотреть файл

@ -62,7 +62,7 @@ function bind_viewer(nodes) {
'<span class="code"> {1}</span></div>', k+1, text));
}
}
$content.html(html.join('')).removeClass('hidden').show();
$content.html(html.join('')).show();
}
if ($diff.length) {
@ -71,7 +71,7 @@ function bind_viewer(nodes) {
var a = dmp.diff_linesToChars_($diff.siblings('.left').text(), $diff.siblings('.right').text());
var diffs = dmp.diff_main(a[0], a[1], false);
dmp.diff_charsToLines_(diffs, a[2]);
$diff.html(dmp.diff_prettyHtml(diffs)).removeClass('hidden').show();
$diff.html(dmp.diff_prettyHtml(diffs)).show();
}
if (window.location.hash) {
@ -93,13 +93,11 @@ function bind_viewer(nodes) {
this.show_leaf = function($leaf) {
/* Exposes the leaves for a given set of node. */
$leaf.removeClass('closed').addClass('open')
.closest('li').next('ul')
.removeClass('hidden').show();
.closest('li').next('ul').show();
};
this.selected = function($link) {
/* Exposes all the leaves to an element */
$link.parentsUntil('ul.root').filter('ul')
.removeClass('hidden').show()
$link.parentsUntil('ul.root').filter('ul').show()
.each(function() {
$(this).prev('li').find('a:first')
.removeClass('closed').addClass('open');
@ -113,7 +111,7 @@ function bind_viewer(nodes) {
var self = this,
$old_wrapper = $('#content-wrapper');
$old_wrapper.hide();
this.nodes.$thinking.removeClass('hidden').show();
this.nodes.$thinking.show();
if (history.pushState !== undefined) {
history.pushState({ path: $link.text() }, '', $link.attr('href'));
}
@ -172,11 +170,17 @@ function bind_viewer(nodes) {
}));
$('#files-prev').click(_pd(function() {
viewer.select(viewer.nodes.$files.find('a.file').eq(viewer.get_selected() - 1));
var prev = viewer.get_selected() - 1
if (prev >= 0) {
viewer.select(viewer.nodes.$files.find('a.file').eq(prev));
}
}));
$('#files-next').click(_pd(function() {
viewer.select(viewer.nodes.$files.find('a.file').eq(viewer.get_selected() + 1));
var next = viewer.nodes.$files.find('a.file').eq(viewer.get_selected() + 1);
if (next.length) {
viewer.select(next);
}
}));
$('#files-wrap').click(_pd(function() {
@ -231,6 +235,13 @@ $(document).ready(function() {
viewer.selected(viewer.nodes.$files.find('a.selected'));
viewer.compute($('#content-wrapper'));
});
} else if (json && json.msg) {
$('#extracting').hide();
$.each(json.msg, function(k) {
$('<p>').text(json.msg[k])
.appendTo($('#file-viewer div.error'));
});
$('#file-viewer div.error').show();
} else {
setTimeout(poll_file_extraction, 2000);
}