add addAnnotations api to batch import many annotations, update importAnnotations dialog

This commit is contained in:
j 2013-03-04 15:41:25 +00:00
parent b9a01b2db9
commit 29008d0eae
6 changed files with 158 additions and 56 deletions

View file

@ -4,6 +4,7 @@ import json
import ox import ox
from django.conf import settings from django.conf import settings
from django.db import transaction
from celery.task import task from celery.task import task
import models import models
@ -63,6 +64,33 @@ def update_matches(id, type):
for e in a_matches.all(): for e in a_matches.all():
e.update_matches(models.Annotation.objects.filter(pk=a.id)) e.update_matches(models.Annotation.objects.filter(pk=a.id))
@task(ignore_results=False, queue='default')
def add_annotations(data):
from item.models import Item
from user.models import User
item = Item.objects.get(itemId=data['item'])
layer_id = data['layer']
layer = filter(lambda l: l['id'] == layer_id, settings.CONFIG['layers'])[0]
user = User.objects.get(username=data['user'])
with transaction.commit_on_success():
for a in data['annotations']:
annotation = models.Annotation(
item=item,
layer=layer_id,
user=user,
start=float(a['in']), end=float(a['out']),
value=a['value'])
annotation.save()
#update facets if needed
if filter(lambda f: f['id'] == layer_id, settings.CONFIG['filters']):
item.update_layer_facet(layer_id)
Item.objects.filter(id=item.id).update(modified=annotation.modified)
annotation.item.modified = annotation.modified
annotation.item.update_find()
annotation.item.update_sort()
annotation.item.update_facets()
return True
@task(ignore_results=True, queue='default') @task(ignore_results=True, queue='default')
def update_item(id): def update_item(id):
from item.models import Item from item.models import Item

View file

@ -15,7 +15,7 @@ from item import utils
from item.models import Item from item.models import Item
import models import models
from tasks import update_item from tasks import update_item, add_annotations
def parse_query(data, user): def parse_query(data, user):
query = {} query = {}
@ -157,6 +157,45 @@ def addAnnotation(request):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(addAnnotation, cache=False) actions.register(addAnnotation, cache=False)
@login_required_json
def addAnnotations(request):
'''
param data {
item: itemId,
layer: layerId,
annotations: [{
in: float,
out: float,
value: string
}, ...]
}
return {'status': {'code': int, 'text': string},
'data': {
id: 123, //id of new annotation
...
}
}
'''
data = json.loads(request.POST['data'])
for key in ('item', 'layer', 'annotations'):
if key not in data:
return render_to_json_response(json_response(status=400,
text='invalid data'))
item = get_object_or_404_json(Item, itemId=data['item'])
layer_id = data['layer']
layer = filter(lambda l: l['id'] == layer_id, settings.CONFIG['layers'])[0]
if item.editable(request.user) \
and layer['canAddAnnotations'].get(request.user.get_profile().get_level()):
response = json_response()
data['user'] = request.user.username
t = add_annotations.delay(data)
response['data']['taskId'] = t.task_id
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(addAnnotations, cache=False)
@login_required_json @login_required_json
def removeAnnotation(request): def removeAnnotation(request):

View file

@ -285,7 +285,10 @@ def firefogg_upload(request):
def taskStatus(request): def taskStatus(request):
#FIXME: should check if user has permissions to get status #FIXME: should check if user has permissions to get status
data = json.loads(request.POST['data']) data = json.loads(request.POST['data'])
task_id = data['task_id'] if 'taskId' in data:
task_id = data['taskId']
else:
task_id = data['task_id']
response = task_status(request, task_id) response = task_status(request, task_id)
return render_to_json_response(response) return render_to_json_response(response)
actions.register(taskStatus, cache=False) actions.register(taskStatus, cache=False)

View file

@ -38,7 +38,7 @@
"canEditPlaces": {"student": true, "staff": true, "admin": true}, "canEditPlaces": {"student": true, "staff": true, "admin": true},
"canEditSitePages": {"staff": true, "admin": true}, "canEditSitePages": {"staff": true, "admin": true},
"canEditUsers": {"staff": true, "admin": true}, "canEditUsers": {"staff": true, "admin": true},
"canImportAnnotations": {}, "canImportAnnotations": {"admin": true},
"canManagePlacesAndEvents": {"student": true, "staff": true, "admin": true}, "canManagePlacesAndEvents": {"student": true, "staff": true, "admin": true},
"canManageTitlesAndNames": {"student": true, "staff": true, "admin": true}, "canManageTitlesAndNames": {"student": true, "staff": true, "admin": true},
"canManageUsers": {"staff": true, "admin": true}, "canManageUsers": {"staff": true, "admin": true},

View file

@ -4,18 +4,17 @@
pandora.ui.importAnnotations = function(data) { pandora.ui.importAnnotations = function(data) {
var content = Ox.Element().css({margin: '16px'}), var content = Ox.Element().css({margin: '16px'}),
file, file,
height = 192, height = 128,
layers = pandora.site.layers.filter(function(layer) { layers = pandora.site.layers.filter(function(layer) {
return layer.canAddAnnotations[pandora.user.level]; return layer.canAddAnnotations[pandora.user.level];
}), }),
layer, layer,
width = 384,
srt = [], srt = [],
total = 0, total = 0,
importButton, importButton,
selectLayer, selectLayer,
selectFile, selectFile,
that = Ox.Dialog({ that = pandora.ui.iconDialog({
buttons: [ buttons: [
Ox.Button({ Ox.Button({
id: 'close', id: 'close',
@ -32,20 +31,16 @@ pandora.ui.importAnnotations = function(data) {
}).bindEvent({ }).bindEvent({
click: function() { click: function() {
importButton.hide(); importButton.hide();
selectLayer.hide(); addAnnotations();
selectFile.hide();
addAnnotation();
} }
}) })
], ],
closeButton: true, closeButton: true,
content: content, text: content,
keys: { keys: {
'escape': 'close' 'escape': 'close'
}, },
height: height,
removeOnClose: true, removeOnClose: true,
width: width,
title: 'Import Annotations' title: 'Import Annotations'
}) })
.bindEvent({ .bindEvent({
@ -54,39 +49,50 @@ pandora.ui.importAnnotations = function(data) {
} }
}), }),
$status = $('<div>').css({ $status = $('<div>').css({
padding: '4px' padding: '4px',
}); paddingBottom: '8px'
}).appendTo(content);
function addAnnotation() { setStatus('Please select layer and .srt file')
function addAnnotations() {
var annotations, task;
if (srt.length > 0) { if (srt.length > 0) {
var data = srt.shift(); setStatus('Loading...');
data.text = Ox.sanitizeHTML(data.text) var annotations = srt.filter(function(data) {
.replace(/<br[ /]*?>\n/g, '\n') return !Ox.isUndefined(data['in']) && !Ox.isUndefined(data.out) && data.text;
.replace(/\n\n/g, '<br>\n')
.replace(/\n/g, '<br>\n'); }).map(function(data) {
$status.html( return {
Ox.formatDuration(data['in']) + ' to ' 'in': data['in'],
+ Ox.formatDuration(data.out) + '<br>\n' + data.text out: data.out,
); value: Ox.sanitizeHTML(data.text)
pandora.api.addAnnotation({ .replace(/<br[ /]*?>\n/g, '\n')
'in': data['in'], .replace(/\n\n/g, '<br>\n')
out: data.out, .replace(/\n/g, '<br>\n')
value: data.text, };
});
pandora.api.addAnnotations({
annotations: annotations,
item: pandora.user.ui.item, item: pandora.user.ui.item,
layer: layer layer: layer
}, function(result) { }, function(result) {
if (result.status.code == 200) { if (result.data.taskId) {
addAnnotation(); setStatus('Waiting or server to import annotations...');
pandora.wait(result.data.taskId, function(result) {
if(result.data.status == 'SUCCESS') {
setStatus(annotations.length + ' annotations imported.');
Ox.Request.clearCache(pandora.user.ui.item);
pandora.$ui.contentPanel.replaceElement(
1, pandora.$ui.item = pandora.ui.item()
);
} else {
setStatus('Importing annotations failed.');
}
});
} else { } else {
content.html('Failed'); setStatus('Importing annotations failed.');
} }
}); });
} else {
$status.html(total + ' annotations imported.');
Ox.Request.clearCache(pandora.user.ui.item);
pandora.$ui.contentPanel.replaceElement(
1, pandora.$ui.item = pandora.ui.item()
);
} }
} }
function parseSRT(data) { function parseSRT(data) {
@ -103,14 +109,17 @@ pandora.ui.importAnnotations = function(data) {
} }
return srt; return srt;
} }
content.append($('<div>').css({
padding: '4px', function setStatus(status) {
paddingBottom: '16px' $status.html(status);
}).html('Import annotations from .srt file:')); }
selectLayer = Ox.Select({ selectLayer = Ox.Select({
items: layers, items: layers,
title: 'select...', title: 'Select Layer',
label: 'Layer' })
.css({
margin: '4px 2px 4px 4px'
}) })
.bindEvent({ .bindEvent({
change: function(data) { change: function(data) {
@ -123,28 +132,33 @@ pandora.ui.importAnnotations = function(data) {
}) })
.appendTo(content); .appendTo(content);
selectFile = $('<input>') selectFile = Ox.FileButton({
.attr({ image: 'upload',
type: 'file' lbael: 'File',
title: 'Select SRT...',
width: 156
}) })
.css({ .css({
padding: '8px' margin: '4px 2px 4px 4px'
}) })
.on({ .bindEvent({
change: function(event) { click: function(data) {
if (this.files.length) { if(data.files.length) {
file = this.files[0];
var reader = new FileReader(); var reader = new FileReader();
reader.onloadend = function(event) { reader.onloadend = function(event) {
srt = parseSRT(this.result); srt = parseSRT(this.result);
total = srt.length; total = srt.length;
total && layer && importButton.options({disabled: false}); if(total && layer) {
$status.html( importButton.options({disabled: false});
selectLayer.hide();
selectFile.hide();
}
setStatus(
'File contains ' + total + ' annotation' 'File contains ' + total + ' annotation'
+ (total == 1 ? '' : 's') + '.' + (total == 1 ? '' : 's') + '.'
); );
}; };
reader.readAsText(file); reader.readAsText(data.files[0]);
} else { } else {
srt = []; srt = [];
total = 0; total = 0;
@ -155,6 +169,6 @@ pandora.ui.importAnnotations = function(data) {
} }
}) })
.appendTo(content); .appendTo(content);
content.append($status);
return that; return that;
}; };

View file

@ -1401,7 +1401,7 @@ pandora.beforeunloadWindow = function() {
return "Encoding is currently running\nDo you want to leave this page?"; return "Encoding is currently running\nDo you want to leave this page?";
//prevent error dialogs on unload //prevent error dialogs on unload
pandora.isUnloading = true; pandora.isUnloading = true;
} };
pandora.unloadWindow = function() { pandora.unloadWindow = function() {
/* /*
@ -1439,6 +1439,24 @@ pandora.updateItemContext = function() {
} }
}; };
pandora.wait = function(taskId, callback, timeout) {
var task = {};
timeout = timeout || 5000;
task.timeout = setTimeout(function() {
pandora.api.taskStatus({taskId: taskId}, function(result) {
var t;
if(result.data.status == 'PENDING') {
t = pandora.wait(taskId, callback);
task.timeout = t.timeout;
} else {
callback(result);
}
});
}, 5000);
return task;
};
(function() { (function() {
// Note: getFindState has to run after getListState and getFilterState // Note: getFindState has to run after getListState and getFilterState