Example #1
0
def delete_project(project_id):
    """
    Delete a project with specified project id. Doesn't require a body

    Args:
        project_id -- The ID of the project

    Raises:
        401 if
          * The project doesn't exist
          * Currently logged in user is not admin of this project (read write
            access)

    Returns:
        200 and a json object --
        {
            'msg': 'Project deleted successfully'
        }
    """
    project = maybe_get_project(project_id)  # check privilege and get project
    # Delete the whole image directory
    shutil.rmtree(
        os.path.join(app.config['STATIC_STORAGE_DIR'], str(project.id)))
    db.session.delete(project)
    db.session.commit()

    return construct_msg('Project deleted successfully'), 200
Example #2
0
def upload_image(project_id):
    """
    Upload image to a project

    Args:
        project_id: The id of the project
    """
    project = maybe_get_project(project_id)
    if 'image' in request.files:
        file = request.files['image']
        project = project
        image = Image(project_id=project.id)
        try:
            image.save_image_to_project(file)
        except IOError:
            abort(400, 'Unable to save image')
        project.images.append(image)
        try:
            db.session.add(image)
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            abort(400, 'Failed to add image')
        return construct_msg('Image added successfully'), 200
    else:
        abort(400, 'Missing \'image\' in request')
Example #3
0
def change_privilege():
    """
    Update privilege of a user.
    Requires the requester to login and be an admin.
    Example json:
    {
        "username": "******",
        "privilege": "admin",
    }
    or
    {
        "username": "******",
        "privilege": "annotator",
    }
    Valid privileges are "annotator", "admin"
    Returns:
        401 if current user is not logged in
        403 if current user is not admin
        400 if requested user is not found, privilege name is not valid
        200 if updated successfully

    """
    current_user = get_jwt_identity()
    check_user_admin_privilege(current_user)

    json = request.get_json()
    check_json(json, ('username', 'privilege'))

    username = json['username']

    user = User.query.filter_by(username=username).first()
    if user is None:
        abort(400, 'User %s not found' % username)

    privilege_map = {
        'admin': PrivilegesEnum.ADMIN,
        'annotator': PrivilegesEnum.ANNOTATOR
    }

    privilege = json['privilege']
    if privilege not in privilege_map:
        abort(400, 'Invalid privilege %s' % privilege)

    user.privileges = privilege_map[privilege]
    db.session.commit()

    return construct_msg('Privilege updated to %s successfully' %
                         privilege), 200
Example #4
0
def upload_zip(project_id):
    """
    Upload zip file of images to project

    Args:
        project_id -- id of project to upload zip file to

    Returns:
        HTTP status code and message of zip file upload
    """
    def filter_condition(name):
        split = name.lower().split('.')
        return split and split[-1] in VALID_IMG_EXTENSIONS

    project = maybe_get_project(project_id)
    project_dir = get_project_dir(project)
    if 'zip' in request.files:
        file = request.files['zip']
        extension = parse_and_validate_file_extension(file.filename, {'zip'})
        new_file_name = '%s.%s' % (str(uuid.uuid4()), extension)
        zip_path = os.path.join(project_dir, new_file_name)
        file.save(zip_path)
        zip_file = ZipFile(zip_path)
        image_names = zip_file.namelist()
        name_map = {}
        for image_name in filter(filter_condition, image_names):
            if image_name in name_map:
                continue
            image = Image(project_id=project.id)
            image.save_empty_image(image_name)
            project.images.append(image)
            try:
                db.session.add(image)
                db.session.commit()
            except IntegrityError:
                db.session.rollback()
                abort(400, 'Failed to add image')
            name_map[image_name] = image.image_storage_path

        zip_file.close()
        multiprocessing.Process(target=unzip_process,
                                args=(zip_path, name_map)).start()
        return (
            construct_msg('Zip uploaded successfully, please wait for unzip'),
            200)
    else:
        abort(400, 'Missing \'zip\' in request')
Example #5
0
def update_info():
    """
    Allow user to update personal info: username, email, and password

    Example input json:
    {
        "username": "******",
        "current_password": "******",
        "email": "*****@*****.**",
        "password": "******"
    }

    Will only update provided fields. "current_password" is required.

    Raises:
        400 if new password format is incorrect
        400 if new email format is incorrect
        400 if username or email is duplicate
        401 if the current password is not correct
    """
    current_user = get_jwt_identity()
    json = request.get_json()
    user = User.query.filter_by(username=current_user).first()
    check_json(json, ['current_password'])
    if not user.verify_password(json['current_password']):
        abort(401, 'Current password incorrect')
    new_password = json.get('password', None)
    if new_password and credential_checking(new_password):
        user.set_password(new_password)

    new_email = json.get('email', None)
    if new_password and email_checking(new_email):
        user.email = new_email

    new_username = json.get('username', None)
    if new_username:
        user.username = new_username

    try:
        db.session.commit()
    except IntegrityError:
        abort(400, 'Username or email already exist')

    return construct_msg('Successfully updated'), 200
Example #6
0
def request_permission(project_id):
    """ Request for specific permission to a project

        Request for specified privilege (READ_WRITE or READ_ONLY) to project
        upon receiving a `POST` request to the `/project/<project_id>/request`
        entry point. User must be signed in. User must provide a `message_type`
        to specify the privilege he or she is requesting for.

        `message_type` takes a string of two values: `r` or `rw`,
        where `r` is `READ_ONLY` and `rw` is `READ_WRITE`

        The request message will be sent to all admins of the project. Note that
        an annotator can also send a request of 'rw' permission. The decision of
        whether upgrading the ANNOTATOR to ADMIN and then granting 'rw' permission
        is left to the ADMIN.

        Must supply a jwt-token to verify user status and extract `user_id`.

        Raises:
            400 Error if message_type is not specified
            400 Error if message is not 'r' or 'rw'
            401 Error if not logged in
            404 Error if project does not exist

        Returns:
            201 if success. Will also return `project_id`.
        """

    req = request.get_json()
    check_json(req, ['message_type'])  # check missing keys

    project = Project.query.filter_by(id=project_id).first()
    if project is None:
        abort(404, 'Project with id=%s does not exist' % project_id)

    current_user = get_jwt_identity()
    user = User.query.filter_by(username=current_user).first()

    map_code_to_message_type = {
        'r': MessageTypeEnum.READ_ONLY_PERMISSION_REQUEST,
        'rw': MessageTypeEnum.READ_WRITE_PERMISSION_REQUEST,
    }

    if req['message_type'] not in map_code_to_message_type:
        abort(400, 'Not able to interpret message_type.')
    message_type = map_code_to_message_type[req['message_type']]

    # Get the list of admins to the project
    admins = []
    for permission in project.permissions:
        if permission.access_type == AccessTypeEnum.READ_WRITE:
            admins.append(permission.user)

    message = Message(type=message_type)
    try:
        db.session.add(message)
        user.sent_messages.append(message)
        for admin in admins:
            admin.received_messages.append(message)
        db.session.commit()
    except IntegrityError:
        db.session.rollback()
        abort(400, 'Create message failed.')

    return construct_msg('Message sent successfully'), 200
Example #7
0
def update_user_permission(project_id):
    """ Grant a user with specified privilege to project

        Grant a user with specified privilege (READ_WRITE or READ_ONLY) to
        project
        upon receiving a `PUT`request to the `/project/<project_id>/permissions`
        entry point. User must be signed in as an ADMIN and have READ_WRITE
        permission to the project. User must provide a `username` to specify the
        user that will be granted permissions, and `access_type` to specify the
        type of privilege to grant.

        `access_type` takes a string of two values: `r` or `rw`, where `r` is
        `READ_ONLY` and `rw` is `READ_WRITE`

        Only user with `ADMIN` privilege can have `READ_WRITE` access to
        projects.
        That is, the user indicated by `username` should have an `ADMIN`
        privilege
        (as opposed to `ANNOTATOR` privilege)

        Must supply a jwt-token to verify user status and extract `user_id`.

        Raises:
            400 Error if username or access_type is not specified
            400 Error if access_type is not 'r' or 'rw'
            401 Error if not logged in
            401 Error if user is not an ADMIN
            401 Error if user does not have privilege to update permission
            403 Error if user indicated by `username` has only ANNOTATOR
            privilege
                but `access_type` is 'rw'
            404 Error if user with user_name does not exist

        Returns:
            201 if success. Will also return `project_id`.
        """

    project = maybe_get_project(project_id)  # check privilege and get project

    req = request.get_json()
    check_json(req, ('username', 'access_type'))  # check missing keys

    username = req['username']
    user = User.query.filter_by(username=username).first()
    if user is None:
        abort(404, 'User with username=%s not found' % username)

    map_code_to_access_type = {
        'r': AccessTypeEnum.READ_ONLY,
        'rw': AccessTypeEnum.READ_WRITE,
    }
    if req['access_type'] not in map_code_to_access_type:
        abort(400, 'Not able to interpret access_type.')
    access_type = map_code_to_access_type[req['access_type']]

    # Check if user has already had some sort of permission to the project
    permission = ProjectPermission.query.filter(
        (ProjectPermission.user_id == user.id)
        & (ProjectPermission.project_id == project_id)).first()
    if permission is not None:
        permission.access_type = access_type
        db.session.commit()
        return construct_msg('Permission updated successfully'), 200

    new_permission = ProjectPermission(access_type=access_type)
    project.permissions.append(new_permission)
    user.project_permissions.append(new_permission)
    try:
        db.session.add(new_permission)
        db.session.commit()
    except IntegrityError:
        db.session.rollback()
        abort(400, 'Update permission failed.')

    return construct_msg('Permission added successfully'), 201
Example #8
0
def upload_annotation():
    """
    Upload annotation resource

    Args: None

    Returns:
        HTTP status code on if annotation was uploaded successfully
    """
    req = request.get_json()
    fields_to_check = ['project_id', 'image_id', 'labels']
    for field_to_check in fields_to_check:
        if field_to_check not in req:
            abort(400, 'Missing %s in json' % field_to_check)
    project = maybe_get_project_read_only(req['project_id'])
    image = Image.query.filter_by(
        id=req['image_id'], project_id=project.id).first()
    if not image:
        abort(400, 'Invalid image id')

    # Validate labels
    labels = req['labels']
    fields_to_check = ['label_id']
    for label_obj in labels:
        for field_to_check in fields_to_check:
            if field_to_check not in label_obj:
                abort(400, 'Missing %s in label' % field_to_check)
        label_id = label_obj['label_id']
        label = Label.query.filter_by(
            id=label_id, project_id=project.id).first()
        if not label:
            abort(
                400, 'label id %d not in project %s' % (label_id,
                                                        project.project_name))

    current_user = get_jwt_identity()
    user = User.query.filter_by(username=current_user).first()

    for label_obj in labels:
        data = str.encode(json.dumps(label_obj))
        label_id = label_obj['label_id']
        label = Label.query.filter_by(
            id=label_id, project_id=project.id).first()

        annotation = Annotation.query.filter_by(
            label_id=label_id, image_id=image.id).first()
        if annotation:
            annotation.data = data
        else:
            annotation = Annotation(
                data=data, image_id=image.id, label_id=label_id, vector=b'')
            project.annotations.append(annotation)
            image.is_annotated = True
            image.annotations.append(annotation)
            label.annotations.append(annotation)
            user.annotations.append(annotation)

            annotation.added_at = dt.datetime.utcnow()
            image.modified_at = dt.datetime.utcnow()
            project.modified_at = dt.datetime.utcnow()

    db.session.commit()
    return construct_msg('Annotation saved successfully'), 200