def delete_treenode(request, project_id=None): """ Deletes a treenode. If the skeleton has a single node, deletes the skeleton and its neuron. Returns the parent_id, if any.""" treenode_id = int(request.POST.get('treenode_id', -1)) # Raise an Exception if the user doesn't have permission to edit the neuron # the skeleton of the treenode is modeling. can_edit_treenode_or_fail(request.user, project_id, treenode_id) # 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.") # Get the neuron before the skeleton is deleted. It can't be # accessed otherwise anymore. neuron = ClassInstance.objects.get(project_id=project_id, cici_via_b__relation__relation_name='model_of', cici_via_b__class_instance_a=treenode.skeleton) # 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. 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 modeled by the skeleton of the treenode is empty, # delete it. response_on_error = 'Could not delete neuron #%s' % neuron.id _delete_if_empty(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 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 delete_treenode(request, project_id=None): """ Deletes a treenode. If the skeleton has a single node, deletes the skeleton and its neuron. Returns the parent_id, if any.""" treenode_id = int(request.POST.get('treenode_id', -1)) # Raise an exception if the user doesn't have permission to edit the # treenode. can_edit_or_fail(request.user, treenode_id, 'treenode') # Raise an Exception if the user doesn't have permission to edit the neuron # the skeleton of the treenode is modeling. can_edit_treenode_or_fail(request.user, project_id, treenode_id) # Make sure the back-end is in the expected state state.validate_state(treenode_id, request.POST.get('state'), lock=True, neighborhood=True) treenode = Treenode.objects.get(pk=treenode_id) parent_id = treenode.parent_id # Get information about linked connectors links = list( TreenodeConnector.objects.filter(project_id=project_id, treenode_id=treenode_id).values_list( 'id', 'relation_id', 'connector_id', 'confidence')) n_sampler_refs = SamplerInterval.objects.filter(start_node=treenode).count() + \ SamplerInterval.objects.filter(end_node=treenode).count() if (n_sampler_refs > 0): raise ValueError( "Can't delete node, it is used in at least one sampler interval") response_on_error = '' deleted_neuron = False try: if not parent_id: children = [] # 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 = "Could not delete root node" 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.") # Get the neuron before the skeleton is deleted. It can't be # accessed otherwise anymore. neuron = ClassInstance.objects.get( project_id=project_id, cici_via_b__relation__relation_name='model_of', cici_via_b__class_instance_a=treenode.skeleton) # 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. 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: # deletes as well treenodes that refer to the skeleton ClassInstance.objects.filter(pk=treenode.skeleton_id) \ .delete() else: return JsonResponse({"error": "Can't delete " \ "isolated node: erroneously, its skeleton contains more " \ "than one treenode! Check for multiple root nodes."}) # If the neuron modeled by the skeleton of the treenode is empty, # delete it. response_on_error = 'Could not delete neuron #%s' % neuron.id deleted_neuron = _delete_if_empty(neuron.id) if deleted_neuron: # Insert log entry for neuron deletion insert_into_log( project_id, request.user.id, 'remove_neuron', (treenode.location_x, treenode.location_y, treenode.location_z), 'Deleted neuron %s and skeleton(s) %s.' % (neuron.id, treenode.skeleton_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' cursor = connection.cursor() cursor.execute( """ UPDATE treenode SET parent_id = %s WHERE project_id = %s AND parent_id = %s RETURNING id, edition_time """, (treenode.parent_id, project_id, treenode.id)) # Children will be a list of two-element lists, just what we want to # return as child info. children = cursor.fetchall() # Remove treenode response_on_error = 'Could not delete treenode.' Treenode.objects.filter(project_id=project_id, pk=treenode_id).delete() return JsonResponse({ 'x': treenode.location_x, 'y': treenode.location_y, 'z': treenode.location_z, 'parent_id': parent_id, 'children': children, 'links': links, 'radius': treenode.radius, 'confidence': treenode.confidence, 'skeleton_id': treenode.skeleton_id, 'deleted_neuron': deleted_neuron, 'success': "Removed treenode successfully." }) 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))
def delete_treenode(request, project_id=None): """ Deletes a treenode. If the skeleton has a single node, deletes the skeleton and its neuron. Returns the parent_id, if any.""" treenode_id = int(request.POST.get('treenode_id', -1)) # Raise an exception if the user doesn't have permission to edit the # treenode. can_edit_or_fail(request.user, treenode_id, 'treenode') # Raise an Exception if the user doesn't have permission to edit the neuron # the skeleton of the treenode is modeling. can_edit_treenode_or_fail(request.user, project_id, treenode_id) # Make sure the back-end is in the expected state state.validate_state(treenode_id, request.POST.get('state'), lock=True, neighborhood=True) treenode = Treenode.objects.get(pk=treenode_id) parent_id = treenode.parent_id # Get information about linked connectors links = list(TreenodeConnector.objects.filter(project_id=project_id, treenode_id=treenode_id).values_list('id', 'relation_id', 'connector_id', 'confidence')) n_sampler_refs = SamplerInterval.objects.filter(start_node=treenode).count() + \ SamplerInterval.objects.filter(end_node=treenode).count() if (n_sampler_refs > 0): raise ValueError("Can't delete node, it is used in at least one sampler interval") response_on_error = '' deleted_neuron = False cursor = connection.cursor() try: if not parent_id: children = [] # 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 = "Could not delete root node" 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.") # Get the neuron before the skeleton is deleted. It can't be # accessed otherwise anymore. neuron = ClassInstance.objects.get(project_id=project_id, cici_via_b__relation__relation_name='model_of', cici_via_b__class_instance_a=treenode.skeleton) # 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. 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: # deletes as well treenodes that refer to the skeleton ClassInstance.objects.filter(pk=treenode.skeleton_id) \ .delete() else: return JsonResponse({"error": "Can't delete " \ "isolated node: erroneously, its skeleton contains more " \ "than one treenode! Check for multiple root nodes."}) # If the neuron modeled by the skeleton of the treenode is empty, # delete it. response_on_error = 'Could not delete neuron #%s' % neuron.id deleted_neuron = _delete_if_empty(neuron.id) if deleted_neuron: # Insert log entry for neuron deletion insert_into_log(project_id, request.user.id, 'remove_neuron', (treenode.location_x, treenode.location_y, treenode.location_z), 'Deleted neuron %s and skeleton(s) %s.' % (neuron.id, treenode.skeleton_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' cursor.execute(""" UPDATE treenode SET parent_id = %s WHERE project_id = %s AND parent_id = %s RETURNING id, edition_time """, (treenode.parent_id, project_id, treenode.id)) # Children will be a list of two-element lists, just what we want to # return as child info. children = cursor.fetchall() # Remove treenode. Set the current user name in a transaction local # variable. This is done to communicate the current user to the trigger # that updates the skeleton summary table. response_on_error = 'Could not delete treenode.' cursor.execute("SET LOCAL catmaid.user_id=%(user_id)s", { 'user_id': request.user.id, }) Treenode.objects.filter(project_id=project_id, pk=treenode_id).delete() return JsonResponse({ 'x': treenode.location_x, 'y': treenode.location_y, 'z': treenode.location_z, 'parent_id': parent_id, 'children': children, 'links': links, 'radius': treenode.radius, 'confidence': treenode.confidence, 'skeleton_id': treenode.skeleton_id, 'deleted_neuron': deleted_neuron, 'success': "Removed treenode successfully." }) except Exception as e: raise Exception(response_on_error + ': ' + str(e))
def _join_skeleton(user, from_treenode_id, to_treenode_id, project_id, annotation_set): """ 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. All annotations in annotation_set will be linked to the 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: from_treenode_id = int(from_treenode_id) to_treenode_id = int(to_treenode_id) try: from_treenode = Treenode.objects.get(pk=from_treenode_id) except Treenode.DoesNotExist: raise Exception("Could not find a skeleton for treenode #%s" % from_treenode_id) try: to_treenode = Treenode.objects.get(pk=to_treenode_id) except Treenode.DoesNotExist: raise Exception("Could not find a skeleton for treenode #%s" % to_treenode_id) from_skid = from_treenode.skeleton_id from_neuron = _get_neuronname_from_skeletonid( project_id, from_skid ) to_skid = to_treenode.skeleton_id to_neuron = _get_neuronname_from_skeletonid( project_id, to_skid ) # Make sure the user has permissions to edit both neurons can_edit_class_instance_or_fail( user, from_neuron['neuronid'], 'neuron') can_edit_class_instance_or_fail( user, to_neuron['neuronid'], 'neuron') # Check if annotations are valid if not check_annotations_on_join(project_id, user, from_neuron['neuronid'], to_neuron['neuronid'], annotation_set): raise Exception("Annotation distribution is not valid for joining. " \ "Annotations for which you don't have permissions have to be kept!") if from_skid == to_skid: raise Exception('Cannot join treenodes of the same skeleton, this would introduce a loop.') # 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) # 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 'losing' neuron if it is empty _delete_if_empty(to_neuron['neuronid']) # 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) # Update linked annotations of neuron response_on_error = 'Could not update annotations of neuron ' \ 'with ID %s' % from_neuron['neuronid'] _update_neuron_annotations(project_id, user, from_neuron['neuronid'], annotation_set) 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, annotations: %s)' % \ (to_skid, to_neuron['neuronname'], from_skid, from_neuron['neuronname'], ', '.join(annotation_set))) except Exception as e: raise Exception(response_on_error + ':' + str(e))
def delete_treenode(request, project_id=None): """ Deletes a treenode. If the skeleton has a single node, deletes the skeleton and its neuron. Returns the parent_id, if any.""" treenode_id = int(request.POST.get('treenode_id', -1)) # Raise an exception if the user doesn't have permission to edit the # treenode. can_edit_or_fail(request.user, treenode_id, 'treenode') # Raise an Exception if the user doesn't have permission to edit the neuron # the skeleton of the treenode is modeling. can_edit_treenode_or_fail(request.user, project_id, treenode_id) treenode = Treenode.objects.get(pk=treenode_id) parent_id = treenode.parent_id response_on_error = '' deleted_neuron = False try: 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 = "Could not delete root node" 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.") # Get the neuron before the skeleton is deleted. It can't be # accessed otherwise anymore. neuron = ClassInstance.objects.get( project_id=project_id, cici_via_b__relation__relation_name='model_of', cici_via_b__class_instance_a=treenode.skeleton) # 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. 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: # deletes as well treenodes that refer to the skeleton ClassInstance.objects.filter(pk=treenode.skeleton_id) \ .delete() 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 modeled by the skeleton of the treenode is empty, # delete it. response_on_error = 'Could not delete neuron #%s' % neuron.id deleted_neuron = _delete_if_empty(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({ 'deleted_neuron': deleted_neuron, 'parent_id': parent_id, 'skeleton_id': treenode.skeleton_id, 'success': "Removed treenode successfully." })) except Exception as e: raise Exception(response_on_error + ': ' + str(e))