diff --git a/django/applications/catmaid/control/volume.py b/django/applications/catmaid/control/volume.py
index d09d4cf306..1a2f79f3c2 100644
--- a/django/applications/catmaid/control/volume.py
+++ b/django/applications/catmaid/control/volume.py
@@ -8,6 +8,7 @@
from django.conf import settings
from catmaid.control.authentication import requires_user_role, user_can_edit
+from catmaid.control.common import get_request_list
from catmaid.models import UserRole, Project, Volume
from catmaid.serializers import VolumeSerializer
@@ -728,3 +729,34 @@ def intersects(request, project_id, volume_id):
return JsonResponse({
'intersects': result[0]
})
+
+@api_view(['POST'])
+@requires_user_role([UserRole.Browse])
+def get_volume_entities(request, project_id):
+ """Retrieve a mapping of volume IDs to entity (class instance) IDs.
+ ---
+ parameters:
+ - name: volume_ids
+ description: A list of volume IDs to map
+ paramType: query
+ """
+ volume_ids = get_request_list(request.POST, 'volume_ids', map_fn=int)
+
+ cursor = connection.cursor()
+ cursor.execute("""
+ SELECT vci.volume_id, vci.class_instance_id
+ FROM volume_class_instance vci
+ JOIN UNNEST(%(volume_ids)s::int[]) volume(id)
+ ON volume.id = vci.volume_id
+ WHERE project_id = %(project_id)s
+ AND relation_id = (
+ SELECT id FROM relation
+ WHERE relation_name = 'model_of'
+ AND project_id = %(project_id)s
+ )
+ """, {
+ 'volume_ids': volume_ids,
+ 'project_id': project_id
+ })
+
+ return JsonResponse(dict(cursor.fetchall()))
diff --git a/django/applications/catmaid/static/js/widgets/volumewidget.js b/django/applications/catmaid/static/js/widgets/volumewidget.js
index 43c3751ddd..efe517942d 100644
--- a/django/applications/catmaid/static/js/widgets/volumewidget.js
+++ b/django/applications/catmaid/static/js/widgets/volumewidget.js
@@ -85,6 +85,12 @@
openFile.onclick = hiddenFileButton.click.bind(hiddenFileButton);
controls.appendChild(openFile);
+ var annotate = document.createElement('button');
+ annotate.appendChild(document.createTextNode('Annotate'));
+ annotate.setAttribute('title', 'Annotate all selected volumes');
+ annotate.onclick = this.annotateSelectedVolumes.bind(this);
+ controls.appendChild(annotate);
+
let self = this;
CATMAID.DOM.appendNumericField(
controls,
@@ -144,7 +150,7 @@
table.style.width = "100%";
var header = table.createTHead();
var hrow = header.insertRow(0);
- var columns = ['Name', 'Id', 'Comment', 'User', 'Creation time',
+ var columns = ['', 'Name', 'Id', 'Comment', 'User', 'Creation time',
'Editor', 'Edition time', 'Action'];
columns.forEach(function(c) {
hrow.insertCell().appendChild(document.createTextNode(c));
@@ -171,6 +177,12 @@
.catch(CATMAID.handleError);
},
columns: [
+ {
+ render: function(data, type, row, meta) {
+ return '';
+ }
+ },
{data: "title"},
{data: "id"},
{data: "comment"},
@@ -197,6 +209,12 @@
'Export STL'
}
],
+ })
+ .on('change', 'input[data-role=select]', function() {
+ var table = $(this).closest('table');
+ var tr = $(this).closest('tr');
+ var data = $(table).DataTable().row(tr).data();
+ data.selected = this.checked;
});
// Remove volume if 'remove' was clicked
@@ -331,7 +349,7 @@
// Display a volume if clicked
var self = this;
- $(table).on('click', 'tbody td', function() {
+ $(table).on('dblclick', 'tbody td', function() {
var tr = $(this).closest("tr");
var volume = self.datatable.row(tr).data();
self.loadVolume(volume.id)
@@ -623,6 +641,39 @@
};
+ /**
+ * Annotate all currently selected volumes.
+ */
+ VolumeManagerWidget.prototype.annotateSelectedVolumes = function() {
+ if (!this.datatable) {
+ return;
+ }
+
+ let allVolumes = this.datatable.rows({'search': 'applied' }).data().toArray();
+ let selectedVolumeIds = allVolumes.filter(function(v) {
+ return v.selected;
+ }).map(function(v) {
+ return v.id;
+ });
+
+ if (selectedVolumeIds.length === 0) {
+ CATMAID.warn("No volumes selected");
+ return;
+ }
+
+ // Retrieve class instance IDs for volumes
+ CATMAID.fetch(project.id + '/volumes/entities/', 'POST', {
+ volume_ids: selectedVolumeIds
+ })
+ .then(function(ciMapping) {
+ return CATMAID.annotate(Object.values(ciMapping));
+ })
+ .then(function() {
+ CATMAID.msg("Success", "Annotations added");
+ })
+ .catch(CATMAID.handleError);
+ };
+
var getVolumeType = function(volume) {
if (volume instanceof CATMAID.AlphaShapeVolume) {
return "alphashape";
diff --git a/django/applications/catmaid/urls.py b/django/applications/catmaid/urls.py
index 3cf6100240..ef758e1679 100644
--- a/django/applications/catmaid/urls.py
+++ b/django/applications/catmaid/urls.py
@@ -495,6 +495,7 @@
url(r'^(?P\d+)/volumes/$', volume.volume_collection),
url(r'^(?P\d+)/volumes/add$', record_view("volumes.create")(volume.add_volume)),
url(r'^(?P\d+)/volumes/import$', volume.import_volumes),
+ url(r'^(?P\d+)/volumes/entities/$', volume.get_volume_entities),
url(r'^(?P\d+)/volumes/(?P\d+)/$', volume.volume_detail),
url(r'^(?P\d+)/volumes/(?P\d+)/intersect$', volume.intersects),
url(r'^(?P\d+)/volumes/(?P\d+)/export\.(?P\w+)', volume.export_volume),