예제 #1
0
파일: node.py 프로젝트: fzadow/CATMAID
def _update(Kind, table, nodes, now, user):
    if not nodes:
        return
    # 0: id
    # 1: X
    # 2: Y
    # 3: Z
    can_edit_all_or_fail(user, (node[0] for node in nodes.itervalues()), table)
    for node in nodes.itervalues():
        Kind.objects.filter(id=int(node[0])).update(editor=user,
                                                    edition_time=now,
                                                    location_x=float(node[1]),
                                                    location_y=float(node[2]),
                                                    location_z=float(node[3]))
예제 #2
0
파일: node.py 프로젝트: aschampion/CATMAID
def _update(Kind, table, nodes, now, user):
    if not nodes:
        return
    # 0: id
    # 1: X
    # 2: Y
    # 3: Z
    can_edit_all_or_fail(user, (node[0] for node in nodes.itervalues()), table)
    for node in nodes.itervalues():
        Kind.objects.filter(id=int(node[0])).update(
            editor=user,
            edition_time=now,
            location_x=float(node[1]),
            location_y=float(node[2]),
            location_z=float(node[3]))
예제 #3
0
파일: node.py 프로젝트: loftiskg/CATMAID
def _update_location(table, nodes, now, user, cursor):
    if not nodes:
        return
    # 0: id
    # 1: X
    # 2: Y
    # 3: Z
    can_edit_all_or_fail(user, (node[0] for node in nodes), table)

    # Sanitize node details
    nodes = [(int(i), float(x), float(y), float(z)) for i, x, y, z in nodes]

    node_template = "(" + "),(".join(["%s, %s, %s, %s"] * len(nodes)) + ")"
    node_table = [v for k in nodes for v in k]

    cursor.execute(
        """
        UPDATE location n
        SET editor_id = %s, location_x = target.x,
            location_y = target.y, location_z = target.z
        FROM (SELECT x.id, x.location_x AS old_loc_x,
                     x.location_y AS old_loc_y, x.location_z AS old_loc_z,
                     y.new_loc_x AS x, y.new_loc_y AS y, y.new_loc_z AS z
              FROM location x
              INNER JOIN (VALUES {}) y(id, new_loc_x, new_loc_y, new_loc_z)
              ON x.id = y.id FOR NO KEY UPDATE) target
        WHERE n.id = target.id
        RETURNING n.id, n.edition_time, target.old_loc_x, target.old_loc_y,
                  target.old_loc_z
    """.format(node_template), [user.id] + node_table)

    updated_rows = cursor.fetchall()
    if len(nodes) != len(updated_rows):
        raise ValueError('Coudn\'t update node ' + ','.join(
            frozenset([str(r[0]) for r in nodes]) -
            frozenset([str(r[0]) for r in updated_rows])))
    return updated_rows
예제 #4
0
def _update_location(table, nodes, now, user, cursor):
    if not nodes:
        return
    # 0: id
    # 1: X
    # 2: Y
    # 3: Z
    can_edit_all_or_fail(user, (node[0] for node in nodes), table)

    # Sanitize node details
    nodes = [(int(i), float(x), float(y), float(z)) for i,x,y,z in nodes]

    node_template = "(" + "),(".join(["%s, %s, %s, %s"] * len(nodes)) + ")"
    node_table = [v for k in nodes for v in k]

    cursor.execute("""
        UPDATE location n
        SET editor_id = %s, location_x = target.x,
            location_y = target.y, location_z = target.z
        FROM (SELECT x.id, x.location_x AS old_loc_x,
                     x.location_y AS old_loc_y, x.location_z AS old_loc_z,
                     y.new_loc_x AS x, y.new_loc_y AS y, y.new_loc_z AS z
              FROM location x
              INNER JOIN (VALUES {}) y(id, new_loc_x, new_loc_y, new_loc_z)
              ON x.id = y.id FOR NO KEY UPDATE) target
        WHERE n.id = target.id
        RETURNING n.id, n.edition_time, target.old_loc_x, target.old_loc_y,
                  target.old_loc_z
    """.format(node_template), [user.id] + node_table)

    updated_rows = cursor.fetchall()
    if len(nodes) != len(updated_rows):
        raise ValueError('Coudn\'t update node ' +
                         ','.join(frozenset([str(r[0]) for r in nodes]) -
                                  frozenset([str(r[0]) for r in updated_rows])))
    return updated_rows
예제 #5
0
def delete_neuron(request, project_id=None, neuron_id=None):
    """ Deletes a neuron if and only if two things are the case: 1. The user
    ownes all treenodes of the skeleton modeling the neuron in question and
    2. The neuron is not annotated by other users.
    """
    # Make sure the user can edit the neuron in general
    can_edit_class_instance_or_fail(request.user, neuron_id, 'neuron')

    # Create class and relation dictionaries
    classes = dict(
        Class.objects.filter(project_id=project_id).values_list(
            'class_name', 'id'))
    relations = dict(
        Relation.objects.filter(project_id=project_id).values_list(
            'relation_name', 'id'))

    # Make sure the user has permission to edit all treenodes of all skeletons
    skeleton_ids = ClassInstanceClassInstance.objects.filter(
        class_instance_b=neuron_id,
        relation_id=relations['model_of']).values_list('class_instance_a',
                                                       flat=True)
    for skid in skeleton_ids:
        others_nodes = Treenode.objects.filter(skeleton_id=skid).exclude(
            user_id=request.user.id).values_list('id', flat=True)
        if others_nodes:
            try:
                can_edit_all_or_fail(request.user, others_nodes, 'treenode')
            except Exception:
                raise Exception("You don't have permission to remove all " \
                        "treenodes of skeleton %s modeling this neuron. The " \
                        "neuron won't be deleted." % skid)

    # Make sure the user has permission to edit all annotations of this neuron
    annotation_ids = set(
        ClassInstanceClassInstance.objects.filter(
            class_instance_a_id=neuron_id,
            relation_id=relations['annotated_with']).values_list('id',
                                                                 flat=True))
    if annotation_ids:
        try:
            can_edit_all_or_fail(request.user, annotation_ids,
                                 'class_instance_class_instance')
        except Exception:
            raise Exception("You don't have permission to remove all " \
                    "annotations linked to this neuron. The neuron won't " \
                    "be deleted.")

    # Try to get the root node to have a valid location for a log entry
    if skeleton_ids:
        try:
            root_node = Treenode.objects.get(skeleton_id=skeleton_ids[0],
                                             parent=None)
            root_location = (root_node.location_x, root_node.location_y,
                             root_node.location_z)
        except (Treenode.DoesNotExist, Treenode.MultipleObjectsReturned):
            root_location = None
    else:
        root_location = None

    # Delete neuron (and implicitely all annotation links due to Django's
    # cascading deletion)
    neuron = get_object_or_404(ClassInstance, pk=neuron_id)
    neuron.delete()

    # Delete all annotations that are not used anymore
    used_annotation_ids = set(
        ClassInstanceClassInstance.objects.filter(
            class_instance_b_id__in=annotation_ids,
            relation_id=relations['annotated_with']).values_list('id',
                                                                 flat=True))
    unused_annotation_ids = annotation_ids.difference(used_annotation_ids)
    ClassInstance.objects.filter(id__in=unused_annotation_ids).delete()

    # Delete the skeletons (and their treenodes through cascading delete)
    cursor = connection.cursor()
    for skid in skeleton_ids:
        # Because there are constraints used in the database that Django is not
        # aware of, it's emulation of cascading deletion doesn't work.
        # Therefore, raw SQL needs to be used to use true cascading deletion.
        cursor.execute(
            '''
        BEGIN;
        DELETE FROM change_request WHERE treenode_id IN (
            SELECT id FROM treenode WHERE skeleton_id=%s AND project_id=%s);
        DELETE FROM change_request WHERE connector_id IN (
            SELECT id FROM treenode_connector WHERE skeleton_id=%s AND project_id=%s);
        DELETE FROM treenode_class_instance WHERE treenode_id IN (
            SELECT id FROM treenode WHERE skeleton_id=%s AND project_id=%s);
        DELETE FROM treenode WHERE skeleton_id=%s AND project_id=%s;
        DELETE FROM treenode_connector WHERE skeleton_id=%s AND project_id=%s;
        DELETE FROM class_instance WHERE id=%s AND project_id=%s;
        DELETE FROM review WHERE skeleton_id=%s AND project_id=%s;
        COMMIT;
        ''', (skid, project_id) * 7)

    # Insert log entry and refer to position of the first skeleton's root node
    insert_into_log(
        project_id, request.user.id, 'remove_neuron', root_location,
        'Deleted neuron %s and skeleton(s) %s.' %
        (neuron_id, ', '.join([str(s) for s in skeleton_ids])))

    return HttpResponse(json.dumps({
        'skeleton_ids': list(skeleton_ids),
        'success': "Deleted neuron #%s as well as its skeletons and " \
                "annotations." % neuron_id}))
예제 #6
0
파일: neuron.py 프로젝트: trautmane/CATMAID
def delete_neuron(request, project_id=None, neuron_id=None):
    """ Deletes a neuron if and only if two things are the case: 1. The user
    ownes all treenodes of the skeleton modeling the neuron in question and
    2. The neuron is not annotated by other users.
    """
    # Make sure the user can edit the neuron in general
    can_edit_class_instance_or_fail(request.user, neuron_id, 'neuron')

    # Create class and relation dictionaries
    classes = dict(Class.objects.filter(
            project_id=project_id).values_list('class_name', 'id'))
    relations = dict(Relation.objects.filter(
            project_id=project_id).values_list('relation_name', 'id'))

    # Make sure the user has permission to edit all treenodes of all skeletons
    skeleton_ids = ClassInstanceClassInstance.objects.filter(
            class_instance_b=neuron_id,
            relation_id=relations['model_of']).values_list(
                    'class_instance_a', flat=True)
    for skid in skeleton_ids:
        others_nodes = Treenode.objects.filter(skeleton_id=skid).exclude(
                user_id=request.user.id).values_list('id', flat=True)
        if others_nodes:
            try:
                can_edit_all_or_fail(request.user, others_nodes, 'treenode')
            except Exception:
                raise Exception("You don't have permission to remove all " \
                        "treenodes of skeleton %s modeling this neuron. The " \
                        "neuron won't be deleted." % skid)

    # Make sure the user has permission to edit all annotations of this neuron
    annotation_ids = set(ClassInstanceClassInstance.objects.filter(
            class_instance_a_id=neuron_id,
            relation_id=relations['annotated_with']).values_list(
                    'id', flat=True))
    if annotation_ids:
        try:
            can_edit_all_or_fail(request.user, annotation_ids,
                    'class_instance_class_instance')
        except Exception:
            raise Exception("You don't have permission to remove all " \
                    "annotations linked to this neuron. The neuron won't " \
                    "be deleted.")

    # Try to get the root node to have a valid location for a log entry
    if skeleton_ids:
        try:
            root_node = Treenode.objects.get(
                    skeleton_id=skeleton_ids[0], parent=None)
            root_location = (root_node.location_x, root_node.location_y,
                             root_node.location_z)
        except (Treenode.DoesNotExist, Treenode.MultipleObjectsReturned):
            root_location = None
    else:
        root_location = None

    # Delete neuron (and implicitely all annotation links due to Django's
    # cascading deletion)
    neuron = get_object_or_404(ClassInstance, pk=neuron_id)
    neuron.delete()

    # Delete all annotations that are not used anymore
    used_annotation_ids = set(ClassInstanceClassInstance.objects.filter(
            class_instance_b_id__in=annotation_ids,
            relation_id=relations['annotated_with']).values_list(
                    'id', flat=True))
    unused_annotation_ids = annotation_ids.difference(used_annotation_ids)
    ClassInstance.objects.filter(id__in=unused_annotation_ids).delete()

    # Delete the skeletons (and their treenodes through cascading delete)
    cursor = connection.cursor()
    for skid in skeleton_ids:
        # Because there are constraints used in the database that Django is not
        # aware of, it's emulation of cascading deletion doesn't work.
        # Therefore, raw SQL needs to be used to use true cascading deletion.
        cursor.execute('''
        BEGIN;
        DELETE FROM change_request WHERE treenode_id IN (
            SELECT id FROM treenode WHERE skeleton_id=%s AND project_id=%s);
        DELETE FROM change_request WHERE connector_id IN (
            SELECT id FROM treenode_connector WHERE skeleton_id=%s AND project_id=%s);
        DELETE FROM treenode_class_instance WHERE treenode_id IN (
            SELECT id FROM treenode WHERE skeleton_id=%s AND project_id=%s);
        DELETE FROM treenode WHERE skeleton_id=%s AND project_id=%s;
        DELETE FROM treenode_connector WHERE skeleton_id=%s AND project_id=%s;
        DELETE FROM class_instance WHERE id=%s AND project_id=%s;
        DELETE FROM review WHERE skeleton_id=%s AND project_id=%s;
        COMMIT;
        ''', (skid, project_id) * 7)

    # Insert log entry and refer to position of the first skeleton's root node
    insert_into_log(project_id, request.user.id, 'remove_neuron', root_location,
            'Deleted neuron %s and skeleton(s) %s.' % (neuron_id,
                    ', '.join([str(s) for s in skeleton_ids])))

    return HttpResponse(json.dumps({
        'skeleton_ids': list(skeleton_ids),
        'success': "Deleted neuron #%s as well as its skeletons and " \
                "annotations." % neuron_id}))
예제 #7
0
def rename_neurons(request: HttpRequest, project_id=None) -> JsonResponse:
    """Rename a neuron.

    If a neuron is not locked by a user on which the current user has no
    permission, the name of neuron can be changed through this endpoint. Neuron
    names are currently not allowed to contain pipe characters ("|").
    ---
    parameters:
        - name: neuron_names
          description: A list of two-element tuples, containing of neuron ID and new name each.
          required: true
          type: array
          paramType: form
    type:
      success:
        description: If renaming was successful
        type: boolean
        required: true
      renamed_neurons:
        description: IDs of the renamed neurons
        type: integer
        required: true
      old_names:
        description: Old names of the renamed neurons
        type: string
        required: true
    """
    neuron_name_pairs = get_request_list(request.POST, 'names')
    if not neuron_name_pairs:
        raise ValueError('Need at least one neuron ID / name mapping')
    neuron_ids = [int(k) for k, _ in neuron_name_pairs]
    neuron_names = [v for _, v in neuron_name_pairs]

    # Make sure the user can edit the neuron
    can_edit_all_or_fail(request.user, neuron_ids, 'class_instance')
    # Do not allow '|' in name because it is used as string separator in NeuroHDF export
    for new_name in neuron_names:
        if '|' in new_name:
            raise ValueError('New name should not contain pipe character')

    cursor = connection.cursor()
    cursor.execute(
        """
        UPDATE class_instance ci
        SET name = new_data.name
        FROM UNNEST(
            %(neuron_ids)s::bigint[],
            %(neuron_names)s::text[]) new_data(id, name)
        JOIN class_instance ci_old
            ON ci_old.id = new_data.id
        WHERE ci.id = new_data.id
            AND ci.project_id = %(project_id)s
        RETURNING ci.id, ci_old.name, ci.name
    """, {
            'project_id': project_id,
            'neuron_ids': neuron_ids,
            'neuron_names': neuron_names,
        })

    rows = cursor.fetchall()
    updated_ids = [r[0] for r in rows]
    old_names = [r[1] for r in rows]
    new_names = [r[2] for r in rows]

    # Insert log entry and return successfully
    if len(updated_ids) == 1:
        insert_into_log(
            project_id, request.user.id, "rename_neuron", None,
            f"Renamed neuron with ID {neuron_ids[0]} from {old_names[0]} to {neuron_names[0]}"
        )
    else:
        insert_into_log(
            project_id, request.user.id, "rename_neuron", None,
            f"Renamed {len(neuron_ids)} neurons. IDs: {', '.join(str(uid) for uid in updated_ids)} Old names: {', '.join(old_names)} New names: {', '.join(new_names)}"
        )

    return JsonResponse({
        'success': True,
        'renamed_neurons': updated_ids,
        'old_names': old_names
    })