Example #1
0
def delete_connector(request:HttpRequest, project_id=None) -> JsonResponse:
    connector_id = int(request.POST.get("connector_id", 0))
    can_edit_or_fail(request.user, connector_id, 'connector')

    # Check provided state
    cursor = connection.cursor()
    state.validate_state(connector_id, request.POST.get('state'),
            node=True, c_links=True, lock=True, cursor=cursor)

    # Get connector and partner information
    connectors = list(Connector.objects.filter(id=connector_id).prefetch_related(
            'treenodeconnector_set', 'treenodeconnector_set__relation'))
    if 1 != len(connectors):
        raise ValueError("Couldn't find exactly one connector with ID #" +
                str(connector_id))
    connector = connectors[0]
    # TODO: Check how many queries here are generated
    partners = [{
        'id': p.treenode_id,
        'edition_time': p.treenode.edition_time,
        'rel': p.relation.relation_name,
        'rel_id': p.relation.id,
        'confidence': p.confidence,
        'link_id': p.id
    } for p in connector.treenodeconnector_set.all()]
    connector.delete()
    return JsonResponse({
        'message': 'Removed connector and class_instances',
        'connector_id': connector_id,
        'confidence': connector.confidence,
        'x': connector.location_x,
        'y': connector.location_y,
        'z': connector.location_z,
        'partners': partners
    })
Example #2
0
 def test_correct_child_state(self):
     ps1 = {
         'children': [[251, '2011-12-05T13:51:36.955Z']],
     }
     s1 = json.dumps(ps1)
     # Expect this state to validate cleanly
     state.validate_state(249, s1, children=True)
Example #3
0
 def test_correct_link_state(self):
     ps1 = {
         'links': [[360, '2011-12-20T10:46:01.360Z'],
                   [372, '2011-12-20T10:46:01.360Z']],
     }
     s1 = json.dumps(ps1)
     state.validate_state(285, s1, links=True)
Example #4
0
def delete_link(request, project_id=None):
    connector_id = int(request.POST.get('connector_id', 0))
    treenode_id = int(request.POST.get('treenode_id', 0))

    cursor = connection.cursor()
    # Make sure the back-end is in the expected state
    state.validate_state([treenode_id, connector_id],
                         request.POST.get('state'),
                         multinode=True,
                         lock=True,
                         cursor=cursor)

    links = TreenodeConnector.objects.filter(
        connector=connector_id,
        treenode=treenode_id).select_related('relation')

    if links.count() == 0:
        raise ValueError('Couldn\'t find link between connector {} '
                         'and node {}'.format(connector_id, treenode_id))

    link = links[0]

    # Could be done by filtering above when obtaining the links,
    # but then one cannot distinguish between the link not existing
    # and the user_id not matching or not being superuser.
    can_edit_or_fail(request.user, link.id, 'treenode_connector')

    deleted_link_id = link.id
    link.delete()
    return JsonResponse({
        'link_id': deleted_link_id,
        'link_type_id': link.relation.id,
        'link_type': link.relation.relation_name,
        'result': 'Removed treenode to connector link'
    })
Example #5
0
def update_parent(request, project_id=None, treenode_id=None):
    treenode_id = int(treenode_id)
    parent_id = int(request.POST.get('parent_id', -1))

    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'),
                         neighborhood=True,
                         lock=True)

    child = get_object_or_404(Treenode, pk=treenode_id, project_id=project_id)
    parent = get_object_or_404(Treenode, pk=parent_id, project_id=project_id)

    if child.skeleton_id != parent.skeleton_id:
        raise Exception("Child node %s is in skeleton %s but parent node %s is in skeleton %s!", \
                        treenode_id, child.skeleton_id, parent_id, parent.skeleton_id)

    child.parent_id = parent_id
    child.save()

    return JsonResponse({
        'success': True,
        'node_id': child.id,
        'parent_id': child.parent_id,
        'skeleton_id': child.skeleton_id
    })
 def test_correct_child_state(self):
     ps1 = {
         'children': [[251, '2011-12-05T13:51:36.955Z']],
     }
     s1 = json.dumps(ps1)
     # Expect this state to validate cleanly
     state.validate_state(249, s1, children=True)
Example #7
0
def node_update(request, project_id=None):
    treenodes = get_request_list(request.POST, "t") or []
    connectors = get_request_list(request.POST, "c") or []

    cursor = connection.cursor()
    nodes = treenodes + connectors
    if nodes:
        node_ids = [int(n[0]) for n in nodes]
        state.validate_state(node_ids,
                             request.POST.get('state'),
                             multinode=True,
                             lock=True,
                             cursor=cursor)

    now = timezone.now()
    old_treenodes = _update_location("treenode", treenodes, now, request.user,
                                     cursor)
    old_connectors = _update_location("connector", connectors, now,
                                      request.user, cursor)

    num_updated_nodes = len(treenodes) + len(connectors)
    return JsonResponse({
        'updated': num_updated_nodes,
        'old_treenodes': old_treenodes,
        'old_connectors': old_connectors
    })
Example #8
0
def delete_connector(request, project_id=None):
    connector_id = int(request.POST.get("connector_id", 0))
    can_edit_or_fail(request.user, connector_id, 'connector')

    # Check provided state
    cursor = connection.cursor()
    state.validate_state(connector_id, request.POST.get('state'),
            node=True, c_links=True, lock=True, cursor=cursor)

    # Get connector and partner information
    connectors = list(Connector.objects.filter(id=connector_id).prefetch_related(
            'treenodeconnector_set', 'treenodeconnector_set__relation'))
    if 1 != len(connectors):
        raise ValueError("Couldn't find exactly one connector with ID #" +
                connector_id)
    connector = connectors[0]
    # TODO: Check how many queries here are generated
    partners = [{
        'id': p.treenode_id,
        'edition_time': p.treenode.edition_time,
        'rel': p.relation.relation_name,
        'rel_id': p.relation.id,
        'confidence': p.confidence,
        'link_id': p.id
    } for p in connector.treenodeconnector_set.all()]
    connector.delete()
    return JsonResponse({
        'message': 'Removed connector and class_instances',
        'connector_id': connector_id,
        'confidence': connector.confidence,
        'x': connector.location_x,
        'y': connector.location_y,
        'z': connector.location_z,
        'partners': partners
    })
Example #9
0
def delete_link(request, project_id=None):
    connector_id = int(request.POST.get('connector_id', 0))
    treenode_id = int(request.POST.get('treenode_id', 0))

    cursor = connection.cursor()
    # Make sure the back-end is in the expected state
    state.validate_state([treenode_id, connector_id], request.POST.get('state'),
            multinode=True, lock=True, cursor=cursor)

    links = TreenodeConnector.objects.filter(
        connector=connector_id,
        treenode=treenode_id).select_related('relation')

    if links.count() == 0:
        raise ValueError('Couldn\'t find link between connector {} '
                'and node {}'.format(connector_id, treenode_id))

    link = links[0]

    # Could be done by filtering above when obtaining the links,
    # but then one cannot distinguish between the link not existing
    # and the user_id not matching or not being superuser.
    can_edit_or_fail(request.user, link.id, 'treenode_connector')

    deleted_link_id = link.id
    link.delete()
    return JsonResponse({
        'link_id': deleted_link_id,
        'link_type_id': link.relation.id,
        'link_type': link.relation.relation_name,
        'result': 'Removed treenode to connector link'
    })
    def test_wrong_child_state(self):
        ps1 = {
            'children': [[247, '2011-12-05T13:51:36.955Z']],
        }
        s1 = json.dumps(ps1)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError,
                          lambda: state.validate_state(249, s1, children=True))

        ps2 = {
            'children': [[251, '1011-12-05T13:51:36.955Z']],
        }
        s2 = json.dumps(ps2)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError,
                          lambda: state.validate_state(249, s2, children=True))

        ps3 = {
            'children': [],
        }
        s3 = json.dumps(ps3)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError,
                          lambda: state.validate_state(249, s3, children=True))

        ps4 = {
            'children': [[251, '2011-12-05T13:51:36.955Z']],
        }
        s4 = json.dumps(ps4)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError,
                          lambda: state.validate_state(251, s4, children=True))
 def test_correct_link_state(self):
     ps1 = {
         'links': [[360, '2011-12-20T10:46:01.360Z'],
                   [372, '2011-12-20T10:46:01.360Z']],
     }
     s1 = json.dumps(ps1)
     state.validate_state(285, s1, links=True)
Example #12
0
    def test_wrong_child_state(self):
        ps1 = {
            'children': [[247, '2011-12-05T13:51:36.955Z']],
        }
        s1 = json.dumps(ps1)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError, lambda: state.validate_state(249, s1, children=True))

        ps2 = {
            'children': [[251, '1011-12-05T13:51:36.955Z']],
        }
        s2 = json.dumps(ps2)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError, lambda: state.validate_state(249, s2, children=True))

        ps3 = {
            'children': [],
        }
        s3 = json.dumps(ps3)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError, lambda: state.validate_state(249, s3, children=True))

        ps4 = {
            'children': [[251, '2011-12-05T13:51:36.955Z']],
        }
        s4 = json.dumps(ps4)
        # Expect this state to validate cleanly
        self.assertRaises(state.StateMatchingError, lambda: state.validate_state(251, s4, children=True))
Example #13
0
def update_parent(request, project_id=None, treenode_id=None):
    treenode_id = int(treenode_id)
    parent_id = int(request.POST.get('parent_id', -1))

    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'),
            neighborhood=True, lock=True)

    child = get_object_or_404(Treenode, pk=treenode_id, project_id=project_id)
    parent = get_object_or_404(Treenode, pk=parent_id, project_id=project_id)

    if child.skeleton_id != parent.skeleton_id:
        raise Exception("Child node %s is in skeleton %s but parent node %s is in skeleton %s!", \
                        treenode_id, child.skeleton_id, parent_id, parent.skeleton_id)

    child.parent_id = parent_id
    child.save()

    return JsonResponse({
        'success': True,
        'node_id': child.id,
        'parent_id': child.parent_id,
        'skeleton_id': child.skeleton_id
    })
Example #14
0
 def test_correct_edge_state(self):
     ps1 = {
         'children': [[251, '2011-12-05T13:51:36.955Z']],
         'edition_time': '2011-12-05T13:51:36.955Z'
     }
     s1 = json.dumps(ps1)
     # Expect this state to validate cleanly
     state.validate_state(249, s1, node=True, children=[251])
 def test_correct_edge_state(self):
     ps1 = {
         'children': [[251, '2011-12-05T13:51:36.955Z']],
         'edition_time': '2011-12-05T13:51:36.955Z'
     }
     s1 = json.dumps(ps1)
     # Expect this state to validate cleanly
     state.validate_state(249, s1, node=True, children=[251])
 def test_correct_neighborhood_state(self):
     ps1 = {
         'edition_time': '2011-12-04T13:51:36.955Z',
         'parent': [283, '2011-12-15T13:51:36.955Z'],
         'children': [[289, '2011-11-06T13:51:36.955Z']],
         'links': [[360, '2011-12-20T10:46:01.360Z']],
     }
     s1 = json.dumps(ps1)
     state.validate_state(285, s1, neighborhood=True)
Example #17
0
 def test_correct_neighborhood_state(self):
     ps1 = {
         'edition_time': '2011-12-04T13:51:36.955Z',
         'parent': [283, '2011-12-15T13:51:36.955Z'],
         'children': [[289, '2011-11-06T13:51:36.955Z']],
         'links': [[360, '2011-12-20T10:46:01.360Z']],
     }
     s1 = json.dumps(ps1)
     state.validate_state(285, s1, neighborhood=True)
Example #18
0
 def test_correct_clink_state(self):
     ps1 = {
         'c_links': [[360, '2011-12-20T10:46:01.360Z'],
                     [372, '3011-12-20T10:46:01.360Z'],
                     [382, '2011-12-20T10:46:01.360Z']],
     }
     s1 = json.dumps(ps1)
     self.assertRaises(ValueError, lambda: state.validate_state(356, s1, links=True))
     self.assertRaises(ValueError, lambda: state.validate_state(358, s1, links=True))
 def test_correct_parent_state(self):
     ps1 = {'parent': [249, '2011-12-05T13:51:36.955Z']}
     s1 = json.dumps(ps1)
     state.validate_state(251, s1, is_parent=True, parent_edittime=True)
     state.validate_state(251, s1, is_parent=True, parent_edittime=False)
     state.validate_state(251, s1, is_parent=False, parent_edittime=True)
     state.validate_state(247, s1, is_parent=False, parent_edittime=True)
     self.assertRaises(
         state.StateMatchingError, lambda: state.validate_state(
             247, s1, is_parent=True, parent_edittime=True))
Example #20
0
 def test_correct_parent_state(self):
     ps1 = {
         'parent': [249, '2011-12-05T13:51:36.955Z']
     }
     s1 = json.dumps(ps1)
     state.validate_state(251, s1, is_parent=True, parent_edittime=True)
     state.validate_state(251, s1, is_parent=True, parent_edittime=False)
     state.validate_state(251, s1, is_parent=False, parent_edittime=True)
     state.validate_state(247, s1, is_parent=False, parent_edittime=True)
     self.assertRaises(state.StateMatchingError,
             lambda: state.validate_state(247, s1, is_parent=True, parent_edittime=True))
Example #21
0
    def test_wrong_parent_state(self):
        ps1 = {
            'parent': [249, '1011-12-05T13:51:36.955Z']
        }
        s1 = json.dumps(ps1)
        self.assertRaises(state.StateMatchingError,
                lambda: state.validate_state(251, s1, is_parent=True, parent_edittime=True))

        s2 = json.dumps([])
        self.assertRaises(state.StateMatchingError,
                lambda: state.validate_state(251, s1, is_parent=True, parent_edittime=True))

        s3 = json.dumps({})
        self.assertRaises(state.StateMatchingError,
                lambda: state.validate_state(251, s3, is_parent=True, parent_edittime=True))
Example #22
0
 def test_wrong_link_state(self):
     ps1 = {
         'links': [[360, '1011-12-20T10:46:01.360Z'],
                   [372, '2011-12-20T10:46:01.360Z']],
     }
     s1 = json.dumps(ps1)
     self.assertRaises(state.StateMatchingError, lambda: state.validate_state(285, s1, links=True))
Example #23
0
 def test_wrong_multinode_shared_state(self):
     ps1 = {
         'edition_time': '2011-12-05T13:51:00.000Z'
     }
     s1 = json.dumps(ps1)
     self.assertRaises(state.StateMatchingError,
             lambda: state.validate_state([247, 249, 251], s1, multinode=False, node=True))
Example #24
0
def update_radii(request, project_id=None):
    """Update the radius of one or more nodes"""
    treenode_ids = [int(v) for k,v in request.POST.items() \
        if k.startswith('treenode_ids[')]
    radii = [float(v) for k,v in request.POST.items() \
        if k.startswith('treenode_radii[')]
    # Make sure the back-end is in the expected state
    cursor = connection.cursor()
    state.validate_state(treenode_ids, request.POST.get('state'),
            multinode=True, lock=True, cursor=cursor)

    updated_nodes = update_node_radii(treenode_ids, radii, cursor)

    return JsonResponse({
        'success': True,
        'updated_nodes': updated_nodes
    })
Example #25
0
    def test_wrong_multinode_state(self):
        ps1 = [
            [247, '2011-12-05T13:51:36.955Z'],
            [249, '3011-12-05T13:51:36.955Z'],
            [251, '2011-12-05T13:51:36.955Z']
        ]
        s1 = json.dumps(ps1)
        self.assertRaises(state.StateMatchingError,
                lambda: state.validate_state([247, 249, 251], s1, multinode=True))

        s2 = json.dumps([])
        self.assertRaises(ValueError,
                lambda: state.validate_state([247, 249, 251], s2, multinode=True))

        s3 = json.dumps({})
        self.assertRaises(ValueError,
                lambda: state.validate_state([247, 249, 251], s3, multinode=True))
Example #26
0
def update_radii(request, project_id=None):
    """Update the radius of one or more nodes"""
    treenode_ids = [int(v) for k,v in six.iteritems(request.POST) \
        if k.startswith('treenode_ids[')]
    radii = [float(v) for k,v in six.iteritems(request.POST) \
        if k.startswith('treenode_radii[')]
    # Make sure the back-end is in the expected state
    cursor = connection.cursor()
    state.validate_state(treenode_ids,
                         request.POST.get('state'),
                         multinode=True,
                         lock=True,
                         cursor=cursor)

    updated_nodes = update_node_radii(treenode_ids, radii, cursor)

    return JsonResponse({'success': True, 'updated_nodes': updated_nodes})
Example #27
0
    def test_correct_multinode_state(self):
        ps1 = [
            [247, '2011-12-05T13:51:36.955Z'],
            [249, '2011-12-05T13:51:36.955Z'],
            [251, '2011-12-05T13:51:36.955Z']
        ]
        s1 = json.dumps(ps1)
        # Expect this state to validate cleanly
        state.validate_state([247, 249, 251], s1, multinode=True)

        # Expect wrong input list to cause error
        self.assertRaises(ValueError,
                lambda: state.validate_state([247, 249], s1, multinode=True))
        self.assertRaises(ValueError,
                lambda: state.validate_state([247, 249, 253], s1, multinode=True))
        self.assertRaises(ValueError,
                lambda: state.validate_state([247, 249, 251, 253], s1, multinode=True))
Example #28
0
def node_update(request, project_id=None):
    treenodes = get_request_list(request.POST, "t") or []
    connectors = get_request_list(request.POST, "c") or []

    cursor = connection.cursor()
    nodes = treenodes + connectors
    if nodes:
        node_ids = [int(n[0]) for n in nodes]
        state.validate_state(node_ids, request.POST.get('state'),
                multinode=True, lock=True, cursor=cursor)

    now = timezone.now()
    old_treenodes = _update_location("treenode", treenodes, now, request.user, cursor)
    old_connectors = _update_location("connector", connectors, now, request.user, cursor)

    num_updated_nodes = len(treenodes) + len(connectors)
    return JsonResponse({
        'updated': num_updated_nodes,
        'old_treenodes': old_treenodes,
        'old_connectors': old_connectors
    })
Example #29
0
def update_parent(request, project_id=None, treenode_id=None):
    treenode_id = int(treenode_id)
    parent_id = int(request.POST.get('parent_id', -1))

    # Make sure the back-end is in the expected state
    state.validate_state(treenode_id, request.POST.get('state'),
            neighborhood=True, lock=True, cursor=cursor)

    child = Treenode.objects.get(pk=treenode_id)
    parent = Treenode.objects.get(pk=parent_id)

    if (child.skeleton_id != parent.skeleton_id):
        raise Exception("Child node %s is in skeleton %s but parent node %s is in skeleton %s!", \
                        treenode_id, child.skeleton_id, parent_id, parent.skeleton_id)

    child.parent_id = parent_id
    child.save()

    return JsonResponse({
        'success': True,
        'node_id': child.id,
        'parent_id': child.parent_id,
        'skeleton_id': child.skeleton_id
    })
Example #30
0
    def test_wrong_neighborhood_state(self):
        ps1 = {
            'edition_time': '2011-12-04T13:51:36.955Z',
            'parent': [283, '2011-12-15T13:51:36.955Z'],
            'children': [[289, '2011-11-06T13:51:36.955Z']],
            'links': [[360, '3011-12-20T10:46:01.360Z']],
        }
        s1 = json.dumps(ps1)
        self.assertRaises(state.StateMatchingError,
                lambda: state.validate_state(285, s1, neighborhood=True))

        ps2 = {
            'parent': [283, '2011-12-15T13:51:36.955Z'],
            'children': [[289, '2011-11-06T13:51:36.955Z']],
            'links': [[360, '3011-12-20T10:46:01.360Z']],
        }
        s2 = json.dumps(ps2)
        self.assertRaises(ValueError,
                lambda: state.validate_state(285, s2, neighborhood=True))

        ps3 = {
            'edition_time': '2011-12-04T13:51:36.955Z',
            'children': [[289, '2011-11-06T13:51:36.955Z']],
            'links': [[360, '3011-12-20T10:46:01.360Z']],
        }
        s3 = json.dumps(ps3)
        self.assertRaises(state.StateMatchingError,
                lambda: state.validate_state(285, s3, neighborhood=True))

        ps4 = {
            'edition_time': '2011-12-04T13:51:36.955Z',
            'parent': [283, '2011-12-15T13:51:36.955Z'],
            'links': [[360, '3011-12-20T10:46:01.360Z']],
        }
        s4 = json.dumps(ps4)
        self.assertRaises(ValueError,
                lambda: state.validate_state(285, s4, neighborhood=True))

        ps5 = {
            'edition_time': '2011-12-04T13:51:36.955Z',
            'parent': [283, '2011-12-15T13:51:36.955Z'],
            'children': [[289, '2011-11-06T13:51:36.955Z']],
        }
        s5 = json.dumps(ps5)
        self.assertRaises(ValueError,
                lambda: state.validate_state(285, s5, neighborhood=True))

        ps6 = {
            'edition_time': '2011-12-04T13:51:36.955Z',
            'parent': [283, '2011-12-15T13:51:36.955Z'],
            'children': [[289, '2011-11-06T13:51:36.955Z']],
            'links': [],
        }
        s6 = json.dumps(ps6)
        self.assertRaises(state.StateMatchingError,
                lambda: state.validate_state(285, s6, neighborhood=True))
 def test_correct_multinode_shared_state(self):
     ps1 = {'edition_time': '2011-12-05T13:51:36.955Z'}
     s1 = json.dumps(ps1)
     state.validate_state([247, 249, 251], s1, multinode=False, node=True)
Example #32
0
def create_link(request, project_id=None):
    """ Create a link between a connector and a treenode

    Currently the following link types (relations) are supported:
    presynaptic_to, postsynaptic_to, abutting, gapjunction_with.
    """
    from_id = int(request.POST.get('from_id', 0))
    to_id = int(request.POST.get('to_id', 0))
    link_type = request.POST.get('link_type', 'none')

    cursor = connection.cursor()
    # Make sure the back-end is in the expected state
    state.validate_state([from_id, to_id],
                         request.POST.get('state'),
                         multinode=True,
                         lock=True,
                         cursor=cursor)

    try:
        project = Project.objects.get(id=project_id)
        relation = Relation.objects.get(project=project,
                                        relation_name=link_type)
        from_treenode = Treenode.objects.get(id=from_id)
        to_connector = Connector.objects.get(id=to_id, project=project)
        links = TreenodeConnector.objects.filter(connector=to_id,
                                                 treenode=from_id,
                                                 relation=relation.id)
    except ObjectDoesNotExist as e:
        return JsonResponse({'error': str(e)})

    if links.count() > 0:
        return JsonResponse({
            'error':
            "A relation '%s' between these two elements already exists!" %
            link_type
        })

    related_skeleton_count = ClassInstance.objects.filter(
        project=project, id=from_treenode.skeleton.id).count()
    if related_skeleton_count > 1:
        # Can never happen. What motivated this check for an error of this kind? Would imply that a treenode belongs to more than one skeleton, which was possible when skeletons owned treendoes via element_of relations rather than by the skeleton_id column.
        return JsonResponse({
            'error':
            'Multiple rows for treenode with ID #%s found' % from_id
        })
    elif related_skeleton_count == 0:
        return JsonResponse({
            'error':
            'Failed to retrieve skeleton id of treenode #%s' % from_id
        })

    if link_type == 'presynaptic_to':
        # Enforce only one presynaptic link
        presyn_links = TreenodeConnector.objects.filter(project=project,
                                                        connector=to_connector,
                                                        relation=relation)
        if (presyn_links.count() != 0):
            return JsonResponse({
                'error':
                'Connector %s does not have zero presynaptic connections.' %
                to_id
            })

    # The object returned in case of success
    result = {}

    if link_type == 'postsynaptic_to':
        # Warn if there is already a link from the source skeleton to the
        # target skeleton. This can happen and is not necessarely wrong, but
        # worth to double check, because it is likely a mistake.
        post_links_to_skeleton = TreenodeConnector.objects.filter(
            project=project,
            connector=to_connector,
            relation=relation,
            skeleton_id=from_treenode.skeleton_id).count()
        if post_links_to_skeleton == 1:
            result['warning'] = 'There is already one post-synaptic ' \
                'connection to the target skeleton'
        elif post_links_to_skeleton > 1:
            result['warning'] = 'There are already %s post-synaptic ' \
                'connections to the target skeleton' % post_links_to_skeleton

        # Enforce only synaptic links
        gapjunction_links = TreenodeConnector.objects.filter(
            project=project,
            connector=to_connector,
            relation__relation_name='gapjunction_with')
        if (gapjunction_links.count() != 0):
            return JsonResponse({
                'error':
                'Connector %s cannot have both a gap junction and a postsynaptic node.'
                % to_id
            })

    if link_type == 'gapjunction_with':
        # Enforce only two gap junction links
        gapjunction_links = TreenodeConnector.objects.filter(
            project=project, connector=to_connector, relation=relation)
        synapse_links = TreenodeConnector.objects.filter(
            project=project,
            connector=to_connector,
            relation__relation_name__endswith='synaptic_to')
        if (gapjunction_links.count() > 1):
            return JsonResponse({
                'error':
                'Connector %s can only have two gap junction connections.' %
                to_id
            })
        if (synapse_links.count() != 0):
            return JsonResponse({
                'error':
                'Connector %s is part of a synapse, and gap junction can not be added.'
                % to_id
            })

    link = TreenodeConnector(
        user=request.user,
        project=project,
        relation=relation,
        treenode=from_treenode,  # treenode_id = from_id
        skeleton=from_treenode.
        skeleton,  # treenode.skeleton_id where treenode.id = from_id
        connector=to_connector  # connector_id = to_id
    )
    link.save()

    result['message'] = 'success'
    result['link_id'] = link.id
    result['link_edition_time'] = link.edition_time
    result['relation_id'] = link.relation_id
    return JsonResponse(result)
Example #33
0
def update_confidence(request, project_id=None, treenode_id=None):
    """Update confidence of edge between a node to either its parent or its
    connectors.

    The connection between a node and its parent or the connectors it is linked
    to can be rated with a confidence value in the range 1-5. If connector links
    should be updated, one can limit the affected connections to a specific
    connector. Returned is an object, mapping updated partners to their old
    confidences.
    ---
    parameters:
      - name: new_confidence
        description: New confidence, value in range 1-5
        type: integer
        required: true
      - name: to_connector
        description: Whether all linked connectors instead of parent should be updated
        type: boolean
        required: false
      - name: partner_ids
        description: Limit update to a set of connectors if to_connector is true
        type: array
        items: integer
        required: false
      - name: partner_confidences
        description: Set different confidences to connectors in <partner_ids>
        type: array
        items: integer
        required: false
    type:
        message:
            type: string
            required: true
        updated_partners:
            type: object
            required: true
    """
    tnid = int(treenode_id)
    can_edit_treenode_or_fail(request.user, project_id, tnid)
    cursor = connection.cursor()

    state.validate_state(tnid, request.POST.get('state'),
            node=True, lock=True, cursor=cursor)

    to_connector = get_request_bool(request.POST, 'to_connector', False)
    partner_ids = get_request_list(request.POST, 'partner_ids', None, int)
    partner_confidences = get_request_list(request.POST, 'partner_confidences',
            None, int)

    new_confidence = int(request.POST.get('new_confidence', 0))

    # If partner confidences are specified, make sure there are exactly as many
    # as there are partners. Otherwise validate passed in confidence
    if partner_ids and partner_confidences:
        if len(partner_confidences) != len(partner_ids):
            raise ValueError("There have to be as many partner confidences as"
                             "there are partner IDs")
    else:
        if new_confidence < 1 or new_confidence > 5:
            raise ValueError('Confidence not in range 1-5 inclusive.')
        if partner_ids:
            # Prepare new confidences for connector query
            partner_confidences = (new_confidence,) * len(partner_ids)

    if to_connector:
        if partner_ids:
            partner_template = ",".join(("(%s,%s)",) * len(partner_ids))
            partner_data = [p for v in zip(partner_ids, partner_confidences) for p in v]
            cursor.execute('''
                UPDATE treenode_connector tc
                SET confidence = target.new_confidence
                FROM (SELECT x.id, x.confidence AS old_confidence,
                             new_values.confidence AS new_confidence
                      FROM treenode_connector x
                      JOIN (VALUES {}) new_values(cid, confidence)
                      ON x.connector_id = new_values.cid
                      WHERE x.treenode_id = %s) target
                WHERE tc.id = target.id
                RETURNING tc.connector_id, tc.edition_time, target.old_confidence
            '''.format(partner_template), partner_data + [tnid])
        else:
            cursor.execute('''
                UPDATE treenode_connector tc
                SET confidence = %s
                FROM (SELECT x.id, x.confidence AS old_confidence
                      FROM treenode_connector x
                      WHERE treenode_id = %s) target
                WHERE tc.id = target.id
                RETURNING tc.connector_id, tc.edition_time, target.old_confidence
            ''', (new_confidence, tnid))
    else:
        cursor.execute('''
            UPDATE treenode t
            SET confidence = %s, editor_id = %s
            FROM (SELECT x.id, x.confidence AS old_confidence
                  FROM treenode x
                  WHERE id = %s) target
            WHERE t.id = target.id
            RETURNING t.parent_id, t.edition_time, target.old_confidence
        ''', (new_confidence, request.user.id, tnid))

    updated_partners = cursor.fetchall()
    if len(updated_partners) > 0:
        location = Location.objects.filter(id=tnid).values_list(
                'location_x', 'location_y', 'location_z')[0]
        insert_into_log(project_id, request.user.id, "change_confidence",
                location, "Changed to %s" % new_confidence)
        return JsonResponse({
            'message': 'success',
            'updated_partners': {
                r[0]: {
                    'edition_time': r[1],
                    'old_confidence': r[2]
                } for r in updated_partners
            }
        })

    # Else, signal error
    if to_connector:
        raise ValueError('Failed to update confidence between treenode %s and '
                'connector.' % tnid)
    else:
        raise ValueError('Failed to update confidence at treenode %s.' % tnid)
Example #34
0
def update_confidence(request, project_id=None, treenode_id=None):
    """Update confidence of edge between a node to either its parent or its
    connectors.

    The connection between a node and its parent or the connectors it is linked
    to can be rated with a confidence value in the range 1-5. If connector links
    should be updated, one can limit the affected connections to a specific
    connector. Returned is an object, mapping updated partners to their old
    confidences.
    ---
    parameters:
      - name: new_confidence
        description: New confidence, value in range 1-5
        type: integer
        required: true
      - name: to_connector
        description: Whether all linked connectors instead of parent should be updated
        type: boolean
        required: false
      - name: partner_ids
        description: Limit update to a set of connectors if to_connector is true
        type: array
        items: integer
        required: false
      - name: partner_confidences
        description: Set different confidences to connectors in <partner_ids>
        type: array
        items: integer
        required: false
    type:
        message:
            type: string
            required: true
        updated_partners:
            type: object
            required: true
    """
    tnid = int(treenode_id)
    can_edit_treenode_or_fail(request.user, project_id, tnid)
    cursor = connection.cursor()

    state.validate_state(tnid,
                         request.POST.get('state'),
                         node=True,
                         lock=True,
                         cursor=cursor)

    to_connector = request.POST.get('to_connector', 'false') == 'true'
    partner_ids = get_request_list(request.POST, 'partner_ids', None, int)
    partner_confidences = get_request_list(request.POST, 'partner_confidences',
                                           None, int)

    new_confidence = int(request.POST.get('new_confidence', 0))

    # If partner confidences are specified, make sure there are exactly as many
    # as there are partners. Otherwise validate passed in confidence
    if partner_ids and partner_confidences:
        if len(partner_confidences) != len(partner_ids):
            raise ValueError("There have to be as many partner confidences as"
                             "there are partner IDs")
    else:
        if new_confidence < 1 or new_confidence > 5:
            raise ValueError('Confidence not in range 1-5 inclusive.')
        if partner_ids:
            # Prepare new confidences for connector query
            partner_confidences = (new_confidence, ) * len(partner_ids)

    if to_connector:
        if partner_ids:
            partner_template = ",".join(("(%s,%s)", ) * len(partner_ids))
            partner_data = [
                p for v in zip(partner_ids, partner_confidences) for p in v
            ]
            cursor.execute(
                '''
                UPDATE treenode_connector tc
                SET confidence = target.new_confidence
                FROM (SELECT x.id, x.confidence AS old_confidence,
                             new_values.confidence AS new_confidence
                      FROM treenode_connector x
                      JOIN (VALUES {}) new_values(cid, confidence)
                      ON x.connector_id = new_values.cid
                      WHERE x.treenode_id = %s) target
                WHERE tc.id = target.id
                RETURNING tc.connector_id, tc.edition_time, target.old_confidence
            '''.format(partner_template), partner_data + [tnid])
        else:
            cursor.execute(
                '''
                UPDATE treenode_connector tc
                SET confidence = %s
                FROM (SELECT x.id, x.confidence AS old_confidence
                      FROM treenode_connector x
                      WHERE treenode_id = %s) target
                WHERE tc.id = target.id
                RETURNING tc.connector_id, tc.edition_time, target.old_confidence
            ''', (new_confidence, tnid))
    else:
        cursor.execute(
            '''
            UPDATE treenode t
            SET confidence = %s, editor_id = %s
            FROM (SELECT x.id, x.confidence AS old_confidence
                  FROM treenode x
                  WHERE id = %s) target
            WHERE t.id = target.id
            RETURNING t.parent_id, t.edition_time, target.old_confidence
        ''', (new_confidence, request.user.id, tnid))

    updated_partners = cursor.fetchall()
    if len(updated_partners) > 0:
        location = Location.objects.filter(id=tnid).values_list(
            'location_x', 'location_y', 'location_z')[0]
        insert_into_log(project_id, request.user.id, "change_confidence",
                        location, "Changed to %s" % new_confidence)
        return JsonResponse({
            'message': 'success',
            'updated_partners': {
                r[0]: {
                    'edition_time': r[1],
                    'old_confidence': r[2]
                }
                for r in updated_partners
            }
        })

    # Else, signal error
    if to_connector:
        raise ValueError('Failed to update confidence between treenode %s and '
                         'connector.' % tnid)
    else:
        raise ValueError('Failed to update confidence at treenode %s.' % tnid)
Example #35
0
def create_treenode(request, project_id=None):
    """
    Add a new treenode to the database
    ----------------------------------

    1. Add new treenode for a given skeleton id. Parent should not be empty.
       return: new treenode id
       If the parent's skeleton has a single node and belongs to the
       'Isolated synaptic terminals' group, then reassign ownership
       of the skeleton and the neuron to the user. The treenode remains
       property of the original user who created it.

    2. Add new treenode (root) and create a new skeleton (maybe for a given
       neuron) return: new treenode id and skeleton id.

    If a neuron id is given, use that one to create the skeleton as a model of
    it.
    """

    params = {}
    float_values = {
            'x': 0,
            'y': 0,
            'z': 0,
            'radius': 0}
    int_values = {
            'confidence': 0,
            'useneuron': -1,
            'parent_id': -1}
    string_values = {}
    for p in float_values.keys():
        params[p] = float(request.POST.get(p, float_values[p]))
    for p in int_values.keys():
        params[p] = int(request.POST.get(p, int_values[p]))
    for p in string_values.keys():
        params[p] = request.POST.get(p, string_values[p])

    # Get optional initial links to connectors, expect each entry to be a list
    # of connector ID, relation ID and confidence.
    links = get_request_list(request.POST, 'links', [], map_fn=int)

    # Make sure the back-end is in the expected state if the node should have a
    # parent and will therefore become part of another skeleton.
    parent_id = int(params['parent_id'])
    has_parent = parent_id and parent_id != -1
    if has_parent:
        state.validate_state(parent_id, request.POST.get('state'),
                parent_edittime=has_parent, lock=True)

    new_treenode = _create_treenode(project_id, request.user, request.user,
            params['x'], params['y'], params['z'], params['radius'],
            params['confidence'], params['useneuron'], params['parent_id'],
            neuron_name=request.POST.get('neuron_name', None))

    # Create all initial links
    if links:
        created_links = create_connector_link(project_id, request.user.id,
                new_treenode.treenode_id, new_treenode.skeleton_id, links)
    else:
        created_links = []

    return JsonResponse({
        'treenode_id': new_treenode.treenode_id,
        'skeleton_id': new_treenode.skeleton_id,
        'edition_time': new_treenode.edition_time,
        'parent_edition_time': new_treenode.parent_edition_time,
        'created_links': created_links
    })
Example #36
0
    except Exception, e:
        raise ValueError("Couldn't parse list parameter: {}".format(e))

    # Make sure the back-end is in the expected state if the node should have a
    # parent and will therefore become part of another skeleton.
    parent_id = params.get('parent_id')
    child_id = params.get('child_id')
    if parent_id not in (-1, None):
        s = request.POST.get('state')
        # Testing egular edge insertion is assumed if a child ID is provided
        partial_child_checks = [] if child_id in (-1, None) else [child_id]
        if takeover_child_ids:
            partial_child_checks.extend(takeover_child_ids)
        state.validate_state(parent_id,
                             s,
                             node=True,
                             children=partial_child_checks or False,
                             lock=True),

    # Find child and parent of new treenode
    child = Treenode.objects.get(pk=params['child_id'])
    parent = Treenode.objects.get(pk=params['parent_id'])

    # Make sure both nodes are actually child and parent
    if not child.parent == parent:
        raise ValueError('The provided nodes need to be child and parent')

    # Make sure the requested location for the new node is on the edge between
    # both existing nodes if the user has no edit permissions on the neuron.
    try:
        can_edit_treenode_or_fail(request.user, project_id, parent.id)
Example #37
0
def insert_treenode(request, project_id=None):
    """
    Create a new treenode between two existing nodes. Its creator and
    creation_date information will be set to information of child node. No node
    will be created, if the node on the edge between the given child and parent
    node.
    """
    # Use creation time, if part of parameter set
    params = {}
    float_values = {
        'x': 0,
        'y': 0,
        'z': 0,
        'radius': 0
    }
    int_values = {
        'confidence': 0,
        'parent_id': -1,
        'child_id': -1
    }
    for p in float_values.keys():
        params[p] = float(request.POST.get(p, float_values[p]))
    for p in int_values.keys():
        params[p] = int(request.POST.get(p, int_values[p]))

    # If siblings should be taken over, all children of the parent node will be
    # come children of the inserted node. This requires extra state
    # information: the child state for the paren.
    takeover_child_ids = get_request_list(request.POST,
            'takeover_child_ids', None, int)

    # Get optional initial links to connectors, expect each entry to be a list
    # of connector ID and relation ID.
    try:
        links = get_request_list(request.POST, 'links', [], int)
    except Exception as e:
        raise ValueError("Couldn't parse list parameter: {}".format(e))

    # Make sure the back-end is in the expected state if the node should have a
    # parent and will therefore become part of another skeleton.
    parent_id = params.get('parent_id')
    child_id = params.get('child_id')
    if parent_id not in (-1, None):
        s = request.POST.get('state')
        # Testing egular edge insertion is assumed if a child ID is provided
        partial_child_checks = [] if child_id in (-1, None) else [child_id]
        if takeover_child_ids:
            partial_child_checks.extend(takeover_child_ids)
        state.validate_state(parent_id, s, node=True,
                children=partial_child_checks or False, lock=True),

    # Find child and parent of new treenode
    child = Treenode.objects.get(pk=params['child_id'])
    parent = Treenode.objects.get(pk=params['parent_id'])

    # Make sure both nodes are actually child and parent
    if not child.parent == parent:
        raise ValueError('The provided nodes need to be child and parent')

    # Make sure the requested location for the new node is on the edge between
    # both existing nodes if the user has no edit permissions on the neuron.
    try:
        can_edit_treenode_or_fail(request.user, project_id, parent.id)
        user, time = request.user, None
    except:
        child_loc = Point3D(child.location_x, child.location_y, child.location_z)
        parent_loc = Point3D(parent.location_x, parent.location_y, parent.location_z)
        new_node_loc = Point3D(params['x'], params['y'], params['z'])
        if not is_collinear(child_loc, parent_loc, new_node_loc, True, 0.001):
            raise ValueError('New node location has to be between child and parent')

        # Use creator and creation time for neighboring node that was created last.
        if child.creation_time < parent.creation_time:
            user, time = parent.user, parent.creation_time
        else:
            user, time = child.user, child.creation_time

    # Create new treenode
    new_treenode = _create_treenode(project_id,
            user, request.user, params['x'], params['y'], params['z'],
            params['radius'], params['confidence'], -1, params['parent_id'], time)

    # Update parent of child to new treenode, do this in raw SQL to also get the
    # updated edition time Update also takeover children
    cursor = connection.cursor()
    params = [new_treenode.treenode_id, child.id]
    if takeover_child_ids:
        params.extend(takeover_child_ids)
        child_template = ",".join(("%s",) * (len(takeover_child_ids) + 1))
    else:
        child_template = "%s"

    cursor.execute("""
        UPDATE treenode SET parent_id = %s
         WHERE id IN ({})
     RETURNING id, edition_time
    """.format(child_template), params)
    result = cursor.fetchall()
    if not result or (len(params) - 1) != len(result):
        raise ValueError("Couldn't update parent of inserted node's child: " + child.id)
    child_edition_times = [[k,v] for k,v in result]

    # Create all initial links
    if links:
        created_links = create_connector_link(project_id, request.user.id,
                new_treenode.treenode_id, new_treenode.skeleton_id, links)
    else:
        created_links = []

    return JsonResponse({
        'treenode_id': new_treenode.treenode_id,
        'skeleton_id': new_treenode.skeleton_id,
        'edition_time': new_treenode.edition_time,
        'parent_edition_time': new_treenode.parent_edition_time,
        'child_edition_times': child_edition_times,
        'created_links': created_links
    })
Example #38
0
def insert_treenode(request, project_id=None):
    """
    Create a new treenode between two existing nodes. Its creator and
    creation_date information will be set to information of child node. No node
    will be created, if the node on the edge between the given child and parent
    node.
    """
    # Use creation time, if part of parameter set
    params = {}
    float_values = {'x': 0, 'y': 0, 'z': 0, 'radius': 0}
    int_values = {'confidence': 0, 'parent_id': -1, 'child_id': -1}
    for p in float_values.keys():
        params[p] = float(request.POST.get(p, float_values[p]))
    for p in int_values.keys():
        params[p] = int(request.POST.get(p, int_values[p]))

    # If siblings should be taken over, all children of the parent node will be
    # come children of the inserted node. This requires extra state
    # information: the child state for the paren.
    takeover_child_ids = get_request_list(request.POST, 'takeover_child_ids',
                                          None, int)

    # Get optional initial links to connectors, expect each entry to be a list
    # of connector ID and relation ID.
    try:
        links = get_request_list(request.POST, 'links', [], int)
    except Exception as e:
        raise ValueError("Couldn't parse list parameter: {}".format(e))

    # Make sure the back-end is in the expected state if the node should have a
    # parent and will therefore become part of another skeleton.
    parent_id = params.get('parent_id')
    child_id = params.get('child_id')
    if parent_id not in (-1, None):
        s = request.POST.get('state')
        # Testing egular edge insertion is assumed if a child ID is provided
        partial_child_checks = [] if child_id in (-1, None) else [child_id]
        if takeover_child_ids:
            partial_child_checks.extend(takeover_child_ids)
        state.validate_state(parent_id,
                             s,
                             node=True,
                             children=partial_child_checks or False,
                             lock=True),

    # Find child and parent of new treenode
    child = Treenode.objects.get(pk=params['child_id'])
    parent = Treenode.objects.get(pk=params['parent_id'])

    # Make sure both nodes are actually child and parent
    if not child.parent == parent:
        raise ValueError('The provided nodes need to be child and parent')

    # Make sure the requested location for the new node is on the edge between
    # both existing nodes if the user has no edit permissions on the neuron.
    try:
        can_edit_treenode_or_fail(request.user, project_id, parent.id)
        user, time = request.user, None
    except:
        child_loc = Point3D(child.location_x, child.location_y,
                            child.location_z)
        parent_loc = Point3D(parent.location_x, parent.location_y,
                             parent.location_z)
        new_node_loc = Point3D(params['x'], params['y'], params['z'])
        if not is_collinear(child_loc, parent_loc, new_node_loc, True, 0.001):
            raise ValueError(
                'New node location has to be between child and parent')

        # Use creator and creation time for neighboring node that was created last.
        if child.creation_time < parent.creation_time:
            user, time = parent.user, parent.creation_time
        else:
            user, time = child.user, child.creation_time

    # Create new treenode
    new_treenode = _create_treenode(project_id, user, request.user,
                                    params['x'], params['y'], params['z'],
                                    params['radius'], params['confidence'], -1,
                                    params['parent_id'], time)

    # Update parent of child to new treenode, do this in raw SQL to also get the
    # updated edition time Update also takeover children
    cursor = connection.cursor()
    params = [new_treenode.treenode_id, child.id]
    if takeover_child_ids:
        params.extend(takeover_child_ids)
        child_template = ",".join(("%s", ) * (len(takeover_child_ids) + 1))
    else:
        child_template = "%s"

    cursor.execute(
        """
        UPDATE treenode SET parent_id = %s
         WHERE id IN ({})
     RETURNING id, edition_time
    """.format(child_template), params)
    result = cursor.fetchall()
    if not result or (len(params) - 1) != len(result):
        raise ValueError("Couldn't update parent of inserted node's child: " +
                         child.id)
    child_edition_times = [[k, v] for k, v in result]

    # Create all initial links
    if links:
        created_links = create_connector_link(project_id, request.user.id,
                                              new_treenode.treenode_id,
                                              new_treenode.skeleton_id, links)
    else:
        created_links = []

    return JsonResponse({
        'treenode_id': new_treenode.treenode_id,
        'skeleton_id': new_treenode.skeleton_id,
        'edition_time': new_treenode.edition_time,
        'parent_edition_time': new_treenode.parent_edition_time,
        'child_edition_times': child_edition_times,
        'created_links': created_links
    })
Example #39
0
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))
Example #40
0
def update_radius(request, project_id=None, treenode_id=None):
    treenode_id = int(treenode_id)
    radius = float(request.POST.get('radius', -1))
    if math.isnan(radius):
        raise Exception("Radius '%s' is not a number!" %
                        request.POST.get('radius'))
    option = int(request.POST.get('option', 0))
    cursor = connection.cursor()
    # Make sure the back-end is in the expected state
    state.validate_state(treenode_id,
                         request.POST.get('state'),
                         node=True,
                         lock=True,
                         cursor=cursor)

    def create_update_response(updated_nodes, radius):
        return JsonResponse({
            'success': True,
            'updated_nodes': updated_nodes,
            'new_radius': radius
        })

    if 0 == option:
        # Update radius only for the passed in treenode and return the old
        # radius.
        old_radii = update_node_radii(treenode_id, radius, cursor)
        return create_update_response(old_radii, radius)

    cursor.execute('''
    SELECT id, parent_id, radius
    FROM treenode
    WHERE skeleton_id = (SELECT t.skeleton_id FROM treenode t WHERE id = %s)
    ''' % treenode_id)

    if 1 == option:
        # Update radius from treenode_id to next branch or end node (included)
        children = defaultdict(list)
        for row in cursor.fetchall():
            children[row[1]].append(row[0])

        include = [treenode_id]
        c = children[treenode_id]
        while 1 == len(c):
            child = c[0]
            include.append(child)
            c = children[child]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 2 == option:
        # Update radius from treenode_id to prev branch node or root (excluded)
        parents = {}
        children = defaultdict(list)
        for row in cursor.fetchall():
            parents[row[0]] = row[1]
            children[row[1]].append(row[0])

        include = [treenode_id]
        parent = parents[treenode_id]
        while parent and parents[parent] and 1 == len(children[parent]):
            include.append(parent)
            parent = parents[parent]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 3 == option:
        # Update radius from treenode_id to prev node with radius (excluded)
        parents = {}
        for row in cursor.fetchall():
            if row[2] < 0 or row[
                    0] == treenode_id:  # DB default radius is 0 but is initialized to -1 elsewhere
                parents[row[0]] = row[1]

        include = [treenode_id]
        parent = parents[treenode_id]
        while parent in parents:
            include.append(parent)
            parent = parents[parent]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 4 == option:
        # Update radius from treenode_id to root (included)
        parents = {row[0]: row[1] for row in cursor.fetchall()}

        include = [treenode_id]
        parent = parents[treenode_id]
        while parent:
            include.append(parent)
            parent = parents[parent]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 5 == option:
        # Update radius of all nodes (in a single query)
        skeleton_id = Treenode.objects.filter(
            pk=treenode_id).values('skeleton_id')
        include = list(Treenode.objects.filter(skeleton_id=skeleton_id) \
                .values_list('id', flat=True))

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)
Example #41
0
 def test_correct_multinode_shared_state(self):
     ps1 = {
         'edition_time': '2011-12-05T13:51:36.955Z'
     }
     s1 = json.dumps(ps1)
     state.validate_state([247, 249, 251], s1, multinode=False, node=True)
Example #42
0
def create_treenode(request, project_id=None):
    """
    Add a new treenode to the database
    ----------------------------------

    1. Add new treenode for a given skeleton id. Parent should not be empty.
       return: new treenode id
       If the parent's skeleton has a single node and belongs to the
       'Isolated synaptic terminals' group, then reassign ownership
       of the skeleton and the neuron to the user. The treenode remains
       property of the original user who created it.

    2. Add new treenode (root) and create a new skeleton (maybe for a given
       neuron) return: new treenode id and skeleton id.

    If a neuron id is given, use that one to create the skeleton as a model of
    it.
    """

    params = {}
    float_values = {'x': 0, 'y': 0, 'z': 0, 'radius': 0}
    int_values = {'confidence': 0, 'useneuron': -1, 'parent_id': -1}
    string_values = {}
    for p in float_values.keys():
        params[p] = float(request.POST.get(p, float_values[p]))
    for p in int_values.keys():
        params[p] = int(request.POST.get(p, int_values[p]))
    for p in string_values.keys():
        params[p] = request.POST.get(p, string_values[p])

    # Get optional initial links to connectors, expect each entry to be a list
    # of connector ID, relation ID and confidence.
    links = get_request_list(request.POST, 'links', [], map_fn=int)

    # Make sure the back-end is in the expected state if the node should have a
    # parent and will therefore become part of another skeleton.
    parent_id = int(params['parent_id'])
    has_parent = parent_id and parent_id != -1
    if has_parent:
        state.validate_state(parent_id,
                             request.POST.get('state'),
                             parent_edittime=has_parent,
                             lock=True)

    new_treenode = _create_treenode(project_id,
                                    request.user,
                                    request.user,
                                    params['x'],
                                    params['y'],
                                    params['z'],
                                    params['radius'],
                                    params['confidence'],
                                    params['useneuron'],
                                    params['parent_id'],
                                    neuron_name=request.POST.get(
                                        'neuron_name', None))

    # Create all initial links
    if links:
        created_links = create_connector_link(project_id, request.user.id,
                                              new_treenode.treenode_id,
                                              new_treenode.skeleton_id, links)
    else:
        created_links = []

    return JsonResponse({
        'treenode_id': new_treenode.treenode_id,
        'skeleton_id': new_treenode.skeleton_id,
        'edition_time': new_treenode.edition_time,
        'parent_edition_time': new_treenode.parent_edition_time,
        'created_links': created_links
    })
Example #43
0
def create_link(request, project_id=None):
    """ Create a link between a connector and a treenode

    Currently the following link types (relations) are supported:
    presynaptic_to, postsynaptic_to, abutting, gapjunction_with,
    tightjunction_with, desmosome_with.
    """
    from_id = int(request.POST.get('from_id', 0))
    to_id = int(request.POST.get('to_id', 0))
    link_type = request.POST.get('link_type', 'none')

    cursor = connection.cursor()
    # Make sure the back-end is in the expected state
    state.validate_state([from_id, to_id], request.POST.get('state'),
            multinode=True, lock=True, cursor=cursor)

    try:
        project = Project.objects.get(id=project_id)
        relation = Relation.objects.get(project=project, relation_name=link_type)
        from_treenode = Treenode.objects.get(id=from_id)
        to_connector = Connector.objects.get(id=to_id, project=project)
        links = TreenodeConnector.objects.filter(
            connector=to_id,
            treenode=from_id,
            relation=relation.id)
    except ObjectDoesNotExist as e:
        return JsonResponse({'error': str(e)})

    if links.count() > 0:
        return JsonResponse({'error': "A relation '%s' between these two elements already exists!" % link_type})

    related_skeleton_count = ClassInstance.objects.filter(project=project, id=from_treenode.skeleton.id).count()
    if related_skeleton_count > 1:
        # Can never happen. What motivated this check for an error of this kind? Would imply that a treenode belongs to more than one skeleton, which was possible when skeletons owned treendoes via element_of relations rather than by the skeleton_id column.
        return JsonResponse({'error': 'Multiple rows for treenode with ID #%s found' % from_id})
    elif related_skeleton_count == 0:
        return JsonResponse({'error': 'Failed to retrieve skeleton id of treenode #%s' % from_id})

    if link_type == 'presynaptic_to':
        # Enforce only one presynaptic link
        presyn_links = TreenodeConnector.objects.filter(project=project, connector=to_connector, relation=relation)
        if (presyn_links.count() != 0):
            return JsonResponse({'error': 'Connector %s does not have zero presynaptic connections.' % to_id})

    # The object returned in case of success
    result = {}

    if link_type == 'postsynaptic_to':
        # Warn if there is already a link from the source skeleton to the
        # target skeleton. This can happen and is not necessarely wrong, but
        # worth to double check, because it is likely a mistake.
        post_links_to_skeleton = TreenodeConnector.objects.filter(project=project,
            connector=to_connector, relation=relation, skeleton_id=from_treenode.skeleton_id).count()
        if post_links_to_skeleton == 1:
            result['warning'] = 'There is already one post-synaptic ' \
                'connection to the target skeleton'
        elif post_links_to_skeleton > 1:
            result['warning'] = 'There are already %s post-synaptic ' \
                'connections to the target skeleton' % post_links_to_skeleton

        # Enforce only synaptic links
        gapjunction_links = TreenodeConnector.objects.filter(project=project, connector=to_connector,
            relation__relation_name='gapjunction_with')
        if (gapjunction_links.count() != 0):
            return JsonResponse({'error': 'Connector %s cannot have both a gap junction and a postsynaptic node.' % to_id})

    if link_type in UNDIRECTED_BINARY_LINK_TYPES:
        # Enforce only two gap junction links
        undirected_links = TreenodeConnector.objects.filter(project=project, connector=to_connector, relation=relation)
        synapse_links = TreenodeConnector.objects.filter(project=project, connector=to_connector, relation__relation_name__endswith='synaptic_to')
        name = LINKS_BY_RELATION[link_type]['name'].lower();
        if (undirected_links.count() > 1):
            return JsonResponse({'error': 'Connector {} can only have two {} connections.'.format(to_id, name)})
        if (synapse_links.count() != 0):
            return JsonResponse({'error': 'Connector {} is part of a synapse, and {} can not be added.'.format(to_id, name)})

    link = TreenodeConnector(
        user=request.user,
        project=project,
        relation=relation,
        treenode=from_treenode,  # treenode_id = from_id
        skeleton=from_treenode.skeleton,  # treenode.skeleton_id where treenode.id = from_id
        connector=to_connector  # connector_id = to_id
    )
    link.save()

    result['message'] = 'success'
    result['link_id'] = link.id
    result['link_edition_time'] = link.edition_time
    result['relation_id'] = link.relation_id
    return JsonResponse(result)
Example #44
0
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))
Example #45
0
    try:
        links = get_request_list(request.POST, 'links', [], lambda x: int(x))
    except Exception, e:
        raise ValueError("Couldn't parse list parameter: {}".format(e))

    # Make sure the back-end is in the expected state if the node should have a
    # parent and will therefore become part of another skeleton.
    parent_id = params.get('parent_id')
    child_id = params.get('child_id')
    if parent_id not in (-1, None):
        s = request.POST.get('state')
        # Testing egular edge insertion is assumed if a child ID is provided
        partial_child_checks = [] if child_id in (-1, None) else [child_id]
        if takeover_child_ids:
            partial_child_checks.extend(takeover_child_ids)
        state.validate_state(parent_id, s, node=True,
                children=partial_child_checks or False, lock=True),

    # Find child and parent of new treenode
    child = Treenode.objects.get(pk=params['child_id'])
    parent = Treenode.objects.get(pk=params['parent_id'])

    # Make sure both nodes are actually child and parent
    if not child.parent == parent:
        raise ValueError('The provided nodes need to be child and parent')

    # Make sure the requested location for the new node is on the edge between
    # both existing nodes if the user has no edit permissions on the neuron.
    try:
        can_edit_treenode_or_fail(request.user, project_id, parent.id)
    except:
        child_loc = Point3D(child.location_x, child.location_y, child.location_z)
Example #46
0
def update_radius(request, project_id=None, treenode_id=None):
    treenode_id = int(treenode_id)
    radius = float(request.POST.get('radius', -1))
    if math.isnan(radius):
        raise Exception("Radius '%s' is not a number!" % request.POST.get('radius'))
    option = int(request.POST.get('option', 0))
    cursor = connection.cursor()
    # Make sure the back-end is in the expected state
    state.validate_state(treenode_id, request.POST.get('state'),
            node=True, lock=True, cursor=cursor)

    def create_update_response(updated_nodes, radius):
        return JsonResponse({
            'success': True,
            'updated_nodes': updated_nodes,
            'new_radius': radius
        })

    if 0 == option:
        # Update radius only for the passed in treenode and return the old
        # radius.
        old_radii = update_node_radii(treenode_id, radius, cursor)
        return create_update_response(old_radii, radius)

    cursor.execute('''
    SELECT id, parent_id, radius
    FROM treenode
    WHERE skeleton_id = (SELECT t.skeleton_id FROM treenode t WHERE id = %s)
    ''' % treenode_id)

    if 1 == option:
        # Update radius from treenode_id to next branch or end node (included)
        children = defaultdict(list)
        for row in cursor.fetchall():
            children[row[1]].append(row[0])

        include = [treenode_id]
        c = children[treenode_id]
        while 1 == len(c):
            child = c[0]
            include.append(child)
            c = children[child]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 2 == option:
        # Update radius from treenode_id to prev branch node or root (excluded)
        parents = {}
        children = defaultdict(list)
        for row in cursor.fetchall():
            parents[row[0]] = row[1]
            children[row[1]].append(row[0])

        include = [treenode_id]
        parent = parents[treenode_id]
        while parent and parents[parent] and 1 == len(children[parent]):
            include.append(parent)
            parent = parents[parent]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 3 == option:
        # Update radius from treenode_id to prev node with radius (excluded)
        parents = {}
        for row in cursor.fetchall():
            if row[2] < 0 or row[0] == treenode_id: # DB default radius is 0 but is initialized to -1 elsewhere
                parents[row[0]] = row[1]

        include = [treenode_id]
        parent = parents[treenode_id]
        while parent in parents:
            include.append(parent)
            parent = parents[parent]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 4 == option:
        # Update radius from treenode_id to root (included)
        parents = {row[0]: row[1] for row in cursor.fetchall()}

        include = [treenode_id]
        parent = parents[treenode_id]
        while parent:
            include.append(parent)
            parent = parents[parent]

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)

    if 5 == option:
        # Update radius of all nodes (in a single query)
        skeleton_id = Treenode.objects.filter(pk=treenode_id).values('skeleton_id')
        include = list(Treenode.objects.filter(skeleton_id=skeleton_id) \
                .values_list('id', flat=True))

        old_radii = update_node_radii(include, radius, cursor)
        return create_update_response(old_radii, radius)