Skip to content

Commit

Permalink
Volume manager: add annotate functionality
Browse files Browse the repository at this point in the history
Add front-end and back-end functionality to add annotations to a set of
volumes. A new checkbox column has been added to the volume table and an
"Annotate" button, that allows to annotate all selected volumes.

Worked on together with @aschampion, @clbarnes and @Willp24.

See #1765
  • Loading branch information
tomka committed Oct 17, 2018
1 parent bf2ce57 commit 6d221bf
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
32 changes: 32 additions & 0 deletions django/applications/catmaid/control/volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()))
55 changes: 53 additions & 2 deletions django/applications/catmaid/static/js/widgets/volumewidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
Expand All @@ -171,6 +177,12 @@
.catch(CATMAID.handleError);
},
columns: [
{
render: function(data, type, row, meta) {
return '<input type="checkbox" data-role="select" ' +
(row.selected ? 'checked' : '') + ' />';
}
},
{data: "title"},
{data: "id"},
{data: "comment"},
Expand All @@ -197,6 +209,12 @@
'<a href="#" data-action="export-STL">Export STL</a>'
}
],
})
.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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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";
Expand Down
1 change: 1 addition & 0 deletions django/applications/catmaid/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@
url(r'^(?P<project_id>\d+)/volumes/$', volume.volume_collection),
url(r'^(?P<project_id>\d+)/volumes/add$', record_view("volumes.create")(volume.add_volume)),
url(r'^(?P<project_id>\d+)/volumes/import$', volume.import_volumes),
url(r'^(?P<project_id>\d+)/volumes/entities/$', volume.get_volume_entities),
url(r'^(?P<project_id>\d+)/volumes/(?P<volume_id>\d+)/$', volume.volume_detail),
url(r'^(?P<project_id>\d+)/volumes/(?P<volume_id>\d+)/intersect$', volume.intersects),
url(r'^(?P<project_id>\d+)/volumes/(?P<volume_id>\d+)/export\.(?P<extension>\w+)', volume.export_volume),
Expand Down

0 comments on commit 6d221bf

Please sign in to comment.