forked from 0x2620/pandora
add addAnnotations api to batch import many annotations, update importAnnotations dialog
This commit is contained in:
parent
b9a01b2db9
commit
29008d0eae
6 changed files with 158 additions and 56 deletions
|
@ -4,6 +4,7 @@ import json
|
|||
import ox
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from celery.task import task
|
||||
|
||||
import models
|
||||
|
@ -63,6 +64,33 @@ def update_matches(id, type):
|
|||
for e in a_matches.all():
|
||||
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')
|
||||
def update_item(id):
|
||||
from item.models import Item
|
||||
|
|
|
@ -15,7 +15,7 @@ from item import utils
|
|||
from item.models import Item
|
||||
|
||||
import models
|
||||
from tasks import update_item
|
||||
from tasks import update_item, add_annotations
|
||||
|
||||
def parse_query(data, user):
|
||||
query = {}
|
||||
|
@ -157,6 +157,45 @@ def addAnnotation(request):
|
|||
return render_to_json_response(response)
|
||||
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
|
||||
def removeAnnotation(request):
|
||||
|
|
|
@ -285,7 +285,10 @@ def firefogg_upload(request):
|
|||
def taskStatus(request):
|
||||
#FIXME: should check if user has permissions to get status
|
||||
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)
|
||||
return render_to_json_response(response)
|
||||
actions.register(taskStatus, cache=False)
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"canEditPlaces": {"student": true, "staff": true, "admin": true},
|
||||
"canEditSitePages": {"staff": true, "admin": true},
|
||||
"canEditUsers": {"staff": true, "admin": true},
|
||||
"canImportAnnotations": {},
|
||||
"canImportAnnotations": {"admin": true},
|
||||
"canManagePlacesAndEvents": {"student": true, "staff": true, "admin": true},
|
||||
"canManageTitlesAndNames": {"student": true, "staff": true, "admin": true},
|
||||
"canManageUsers": {"staff": true, "admin": true},
|
||||
|
|
|
@ -4,18 +4,17 @@
|
|||
pandora.ui.importAnnotations = function(data) {
|
||||
var content = Ox.Element().css({margin: '16px'}),
|
||||
file,
|
||||
height = 192,
|
||||
height = 128,
|
||||
layers = pandora.site.layers.filter(function(layer) {
|
||||
return layer.canAddAnnotations[pandora.user.level];
|
||||
}),
|
||||
layer,
|
||||
width = 384,
|
||||
srt = [],
|
||||
total = 0,
|
||||
importButton,
|
||||
selectLayer,
|
||||
selectFile,
|
||||
that = Ox.Dialog({
|
||||
that = pandora.ui.iconDialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'close',
|
||||
|
@ -32,20 +31,16 @@ pandora.ui.importAnnotations = function(data) {
|
|||
}).bindEvent({
|
||||
click: function() {
|
||||
importButton.hide();
|
||||
selectLayer.hide();
|
||||
selectFile.hide();
|
||||
addAnnotation();
|
||||
addAnnotations();
|
||||
}
|
||||
})
|
||||
],
|
||||
closeButton: true,
|
||||
content: content,
|
||||
text: content,
|
||||
keys: {
|
||||
'escape': 'close'
|
||||
},
|
||||
height: height,
|
||||
removeOnClose: true,
|
||||
width: width,
|
||||
title: 'Import Annotations'
|
||||
})
|
||||
.bindEvent({
|
||||
|
@ -54,39 +49,50 @@ pandora.ui.importAnnotations = function(data) {
|
|||
}
|
||||
}),
|
||||
$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) {
|
||||
var data = srt.shift();
|
||||
data.text = Ox.sanitizeHTML(data.text)
|
||||
.replace(/<br[ /]*?>\n/g, '\n')
|
||||
.replace(/\n\n/g, '<br>\n')
|
||||
.replace(/\n/g, '<br>\n');
|
||||
$status.html(
|
||||
Ox.formatDuration(data['in']) + ' to '
|
||||
+ Ox.formatDuration(data.out) + '<br>\n' + data.text
|
||||
);
|
||||
pandora.api.addAnnotation({
|
||||
'in': data['in'],
|
||||
out: data.out,
|
||||
value: data.text,
|
||||
setStatus('Loading...');
|
||||
var annotations = srt.filter(function(data) {
|
||||
return !Ox.isUndefined(data['in']) && !Ox.isUndefined(data.out) && data.text;
|
||||
|
||||
}).map(function(data) {
|
||||
return {
|
||||
'in': data['in'],
|
||||
out: data.out,
|
||||
value: Ox.sanitizeHTML(data.text)
|
||||
.replace(/<br[ /]*?>\n/g, '\n')
|
||||
.replace(/\n\n/g, '<br>\n')
|
||||
.replace(/\n/g, '<br>\n')
|
||||
};
|
||||
});
|
||||
pandora.api.addAnnotations({
|
||||
annotations: annotations,
|
||||
item: pandora.user.ui.item,
|
||||
layer: layer
|
||||
}, function(result) {
|
||||
if (result.status.code == 200) {
|
||||
addAnnotation();
|
||||
if (result.data.taskId) {
|
||||
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 {
|
||||
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) {
|
||||
|
@ -103,14 +109,17 @@ pandora.ui.importAnnotations = function(data) {
|
|||
}
|
||||
return srt;
|
||||
}
|
||||
content.append($('<div>').css({
|
||||
padding: '4px',
|
||||
paddingBottom: '16px'
|
||||
}).html('Import annotations from .srt file:'));
|
||||
|
||||
function setStatus(status) {
|
||||
$status.html(status);
|
||||
}
|
||||
|
||||
selectLayer = Ox.Select({
|
||||
items: layers,
|
||||
title: 'select...',
|
||||
label: 'Layer'
|
||||
title: 'Select Layer',
|
||||
})
|
||||
.css({
|
||||
margin: '4px 2px 4px 4px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
|
@ -123,28 +132,33 @@ pandora.ui.importAnnotations = function(data) {
|
|||
})
|
||||
.appendTo(content);
|
||||
|
||||
selectFile = $('<input>')
|
||||
.attr({
|
||||
type: 'file'
|
||||
selectFile = Ox.FileButton({
|
||||
image: 'upload',
|
||||
lbael: 'File',
|
||||
title: 'Select SRT...',
|
||||
width: 156
|
||||
})
|
||||
.css({
|
||||
padding: '8px'
|
||||
margin: '4px 2px 4px 4px'
|
||||
})
|
||||
.on({
|
||||
change: function(event) {
|
||||
if (this.files.length) {
|
||||
file = this.files[0];
|
||||
.bindEvent({
|
||||
click: function(data) {
|
||||
if(data.files.length) {
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function(event) {
|
||||
srt = parseSRT(this.result);
|
||||
total = srt.length;
|
||||
total && layer && importButton.options({disabled: false});
|
||||
$status.html(
|
||||
if(total && layer) {
|
||||
importButton.options({disabled: false});
|
||||
selectLayer.hide();
|
||||
selectFile.hide();
|
||||
}
|
||||
setStatus(
|
||||
'File contains ' + total + ' annotation'
|
||||
+ (total == 1 ? '' : 's') + '.'
|
||||
);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
reader.readAsText(data.files[0]);
|
||||
} else {
|
||||
srt = [];
|
||||
total = 0;
|
||||
|
@ -155,6 +169,6 @@ pandora.ui.importAnnotations = function(data) {
|
|||
}
|
||||
})
|
||||
.appendTo(content);
|
||||
content.append($status);
|
||||
|
||||
return that;
|
||||
};
|
||||
|
|
|
@ -1401,7 +1401,7 @@ pandora.beforeunloadWindow = function() {
|
|||
return "Encoding is currently running\nDo you want to leave this page?";
|
||||
//prevent error dialogs on unload
|
||||
pandora.isUnloading = true;
|
||||
}
|
||||
};
|
||||
|
||||
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() {
|
||||
|
||||
// Note: getFindState has to run after getListState and getFilterState
|
||||
|
|
Loading…
Reference in a new issue