def delete_treenode(request, project_id=None): """ If the skeleton has a single node, deletes the skeleton, and if so, if the skeleton is a model_of a neuron that was part_of group 'Isolated synaptic terminals', deletes the neuron. Returns the parent_id, if any.""" treenode_id = int(request.POST.get('treenode_id', -1)) # Raise an Exception if the user doesn't own the treenode or is not superuser can_edit_or_fail(request.user, treenode_id, 'treenode') # treenode = Treenode.objects.get(pk=treenode_id) parent_id = treenode.parent_id response_on_error = '' try: cursor = connection.cursor() if not parent_id: # This treenode is root. response_on_error = 'Could not retrieve children for treenode #%s' % treenode_id n_children = Treenode.objects.filter(parent=treenode).count() response_on_error = "Can't delete root node when it has children" if n_children > 0: # TODO yes you can, the new root is the first of the children, and other children become independent skeletons raise Exception("You can't delete the root node when it has children.") # Remove the original skeleton. # It is OK to remove it if it only had one node, # even if the skeleton's user does not match or the user is not superuser. # Fetch the neuron id, if it was a placeholder under 'Isolated synaptic terminals' group neuron_id = _in_isolated_synaptic_terminals(treenode.skeleton_id) # Delete the skeleton, which triggers deleting the ClassInstanceClassInstance relationship with neuron_id response_on_error = 'Could not delete skeleton.' # Extra check for errors, like having two root nodes count = Treenode.objects.filter(skeleton_id=treenode.skeleton_id).count() if 1 == count: ClassInstance.objects.filter(pk=treenode.skeleton_id).delete() # deletes as well treenodes that refer to the skeleton else: return HttpResponse(json.dumps({"error": "Can't delete isolated node: erroneously, its skeleton contains more than one treenode! Check for multiple root nodes."})) # If the neuron was part of the 'Isolated synaptic terminals' and no other skeleton is a model_of it, delete it if neuron_id: response_on_error = 'Could not delete neuron #%s' % neuron_id if _delete_if_empty(neuron_id): print >> sys.stderr, "DELETED neuron %s from IST" % neuron_id else: # Treenode is not root, it has a parent and perhaps children. # Reconnect all the children to the parent. response_on_error = 'Could not update parent id of children nodes' Treenode.objects.filter(parent=treenode).update(parent=treenode.parent) # Remove treenode response_on_error = 'Could not delete treenode.' Treenode.objects.filter(pk=treenode_id).delete() return HttpResponse(json.dumps({'parent_id': parent_id})) except Exception as e: raise Exception(response_on_error + ': ' + str(e))
def _join_skeleton(user, from_treenode_id, to_treenode_id, project_id): """ Take the IDs of two nodes, each belonging to a different skeleton, and make to_treenode be a child of from_treenode, and join the nodes of the skeleton of to_treenode into the skeleton of from_treenode, and delete the former skeleton of to_treenode.""" if from_treenode_id is None or to_treenode_id is None: raise Exception('Missing arguments to _join_skeleton') response_on_error = '' try: to_treenode_id = int(to_treenode_id) cursor = connection.cursor() cursor.execute(''' SELECT class_instance.user_id, treenode.skeleton_id, treenode.user_id FROM class_instance, treenode WHERE treenode.id = %s AND treenode.skeleton_id = class_instance.id ''' % to_treenode_id) to_skeleton_user_id, to_skid, to_treenode_user_id = cursor.fetchone() # Check if joining is allowed if 0 == Treenode.objects.filter(parent_id=to_treenode_id).count() and Treenode.objects.filter(pk=to_treenode_id).values_list('parent_id')[0][0] is None: # Is an isolated node, so it can be joined freely pass # If the treenode is not isolated, must own the skeleton or be superuser elif user.is_superuser or user.id == to_skeleton_user_id: pass # If the skeleton is a model_of a neuron that is part_of the 'Fragments' group # or the 'Isolated synaptic terminals' group, then it can be joined elif _under_fragments(to_skid): pass # Else, if the user owns the node (but not the skeleton), the join is possible only if all other nodes also belong to the user (such a situation occurs when the user ows both skeletons to join, or when part of a skeleton is split away from a larger one that belongs to someone else) elif user.id == to_treenode_user_id and 0 == Treenode.objects.filter(skeleton_id=to_skid).exclude(user=user).count(): pass else: raise Exception("User %s with id #%s cannot join skeleton #%s, because the user doesn't own the skeleton or the skeleton contains nodes that belong to someone else." % (user.username, user.id, to_skid)) from_treenode_id = int(from_treenode_id) from_treenode = Treenode.objects.get(pk=from_treenode_id) from_skid = from_treenode.skeleton_id if from_skid == to_skid: raise Exception('Cannot join treenodes of the same skeleton, this would introduce a loop.') from_neuron = _get_neuronname_from_skeletonid( project_id, from_skid ) to_neuron = _get_neuronname_from_skeletonid( project_id, to_skid ) # Reroot to_skid at to_treenode if necessary response_on_error = 'Could not reroot at treenode %s' % to_treenode_id _reroot_skeleton(to_treenode_id, project_id) # The target skeleton is removed and its treenode assumes # the skeleton id of the from-skeleton. response_on_error = 'Could not update Treenode table with new skeleton id for joined treenodes.' Treenode.objects.filter(skeleton=to_skid).update(skeleton=from_skid) response_on_error = 'Could not update TreenodeConnector table.' TreenodeConnector.objects.filter( skeleton=to_skid).update(skeleton=from_skid) # Determine if the neuron is part_of group 'Isolated synaptic terminals' response_on_error = 'Could not find neuron of skeleton #%s.' % to_skid neuron_id = _in_isolated_synaptic_terminals(to_skid) # Remove skeleton of to_id (deletes cicic part_of to neuron by cascade, # leaving the parent neuron dangling in the object tree). response_on_error = 'Could not delete skeleton with ID %s.' % to_skid ClassInstance.objects.filter(pk=to_skid).delete() # Remove the neuron if it belongs to 'Isolated synaptic terminals' # It is ok if the request.user doesn't match with the neuron's user_id or is not superuser. if neuron_id: response_on_error = 'Could not delete neuron with id %s.' % neuron_id if _delete_if_empty(neuron_id): print >> sys.stderr, "DELETED neuron %s from IST" % neuron_id # Update the parent of to_treenode. response_on_error = 'Could not update parent of treenode with ID %s' % to_treenode_id Treenode.objects.filter(id=to_treenode_id).update(parent=from_treenode_id, editor=user) insert_into_log(project_id, user.id, 'join_skeleton', from_treenode.location, 'Joined skeleton with ID %s (neuron: %s) into skeleton with ID %s (neuron: %s)' % (to_skid, to_neuron['neuronname'], from_skid, from_neuron['neuronname']) ) except Exception as e: raise Exception(response_on_error + ':' + str(e))