예제 #1
0
파일: api.py 프로젝트: jinxuchen/virtool
async def create(req):
    """
    Add a new user to the user database.

    """
    db = req.app["db"]
    data = await req.json()

    if data["user_id"] == "virtool":
        return bad_request("Reserved user name: virtool")

    error = await virtool.users.checks.check_password_length(req)

    if error:
        return bad_request(error)

    user_id = data["user_id"]

    try:
        document = await virtool.users.db.create(db, user_id, data["password"],
                                                 data["force_reset"])
    except virtool.errors.DatabaseError:
        return bad_request("User already exists")

    headers = {"Location": f"/api/users/{user_id}"}

    return json_response(virtool.utils.base_processor(
        {key: document[key]
         for key in virtool.users.db.PROJECTION}),
                         headers=headers,
                         status=201)
예제 #2
0
파일: api.py 프로젝트: jinxuchen/virtool
async def add_user(req):
    db = req.app["db"]
    data = req["data"]
    ref_id = req.match_info["ref_id"]

    document = await db.references.find_one(ref_id, ["groups", "users"])

    if document is None:
        return not_found()

    if not await virtool.references.db.check_right(req, ref_id, "modify"):
        return insufficient_rights()

    try:
        subdocument = await virtool.references.db.add_group_or_user(
            db, ref_id, "users", data)
    except virtool.errors.DatabaseError as err:
        if "already exists" in str(err):
            return bad_request("User already exists")

        if "does not exist" in str(err):
            return bad_request("User does not exist")

        raise

    headers = {"Location": f"/api/refs/{ref_id}/users/{subdocument['id']}"}

    subdocument = await virtool.users.db.attach_identicons(db, subdocument)

    return json_response(subdocument, headers=headers, status=201)
예제 #3
0
async def create(req):
    """
    Add a new subtraction. Starts an :class:`.CreateSubtraction` job process.

    """
    db = req.app["db"]
    data = req["data"]

    subtraction_id = data["subtraction_id"]

    if await db.subtraction.count({"_id": subtraction_id}):
        return bad_request("Subtraction name already exists")

    file_id = data["file_id"]

    file = await db.files.find_one(file_id, ["name"])

    if file is None:
        return bad_request("File does not exist")

    job_id = await virtool.db.utils.get_new_id(db.jobs)

    user_id = req["client"].user_id

    document = {
        "_id": data["subtraction_id"],
        "nickname": data["nickname"],
        "ready": False,
        "is_host": True,
        "file": {
            "id": file_id,
            "name": file["name"]
        },
        "user": {
            "id": user_id
        },
        "job": {
            "id": job_id
        }
    }

    await db.subtraction.insert_one(document)

    task_args = {"subtraction_id": subtraction_id, "file_id": file_id}

    await virtool.jobs.db.create(db,
                                 req.app["settings"],
                                 "create_subtraction",
                                 task_args,
                                 user_id,
                                 job_id=job_id)

    await req.app["jobs"].enqueue(job_id)

    headers = {"Location": f"/api/account/keys/{subtraction_id}"}

    return json_response(virtool.utils.base_processor(document),
                         headers=headers,
                         status=201)
예제 #4
0
파일: api.py 프로젝트: jinxuchen/virtool
async def edit(req):
    """
    Update specific fields in the sample document.

    """
    db = req.app["db"]
    data = req["data"]

    sample_id = req.match_info["sample_id"]

    if not await virtool.samples.db.check_rights(db, sample_id, req["client"]):
        return insufficient_rights()

    message = await virtool.samples.db.check_name(db,
                                                  req.app["settings"],
                                                  data["name"],
                                                  sample_id=sample_id)

    if message:
        return bad_request(message)

    document = await db.samples.find_one_and_update(
        {"_id": sample_id}, {"$set": data},
        projection=virtool.samples.db.LIST_PROJECTION)

    processed = virtool.utils.base_processor(document)

    return json_response(processed)
예제 #5
0
async def install(req):
    """
    Install the latest official HMM database from GitHub.

    """
    db = req.app["db"]

    user_id = req["client"].user_id

    if await db.status.count({"_id": "hmm", "updates.ready": False}):
        return conflict("Install already in progress")

    process = await virtool.processes.db.register(db, "install_hmms")

    document = await db.status.find_one_and_update(
        {"_id": "hmm"}, {"$set": {
            "process": {
                "id": process["id"]
            }
        }})

    release = document.get("release", None)

    if release is None:
        return bad_request("Target release does not exist")

    update = virtool.github.create_update_subdocument(release, False, user_id)

    await db.status.update_one({"_id": "hmm"}, {"$push": {"updates": update}})

    await aiojobs.aiohttp.spawn(
        req, virtool.hmm.db.install(req.app, process["id"], release, user_id))

    return json_response(update)
예제 #6
0
파일: api.py 프로젝트: jinxuchen/virtool
async def get(req):
    """
    Get a complete analysis document.

    """
    db = req.app["db"]

    analysis_id = req.match_info["analysis_id"]

    document = await db.analyses.find_one(analysis_id)

    if document is None:
        return not_found()

    sample = await db.samples.find_one({"_id": document["sample"]["id"]}, {"quality": False})

    if not sample:
        return bad_request("Parent sample does not exist")

    read, _ = virtool.samples.utils.get_sample_rights(sample, req["client"])

    if not read:
        return insufficient_rights()

    if document["ready"]:
        document = await virtool.analyses.format.format_analysis(db, req.app["settings"], document)

    document["subtraction"] = {
        "id": sample["subtraction"]["id"]
    }

    return json_response(virtool.utils.base_processor(document))
예제 #7
0
파일: api.py 프로젝트: jinxuchen/virtool
async def set_rights(req):
    """
    Change rights settings for the specified sample document.

    """
    db = req.app["db"]
    data = req["data"]

    sample_id = req.match_info["sample_id"]

    if not await db.samples.count({"_id": sample_id}):
        return not_found()

    user_id = req["client"].user_id

    # Only update the document if the connected user owns the samples or is an administrator.
    if not req[
            "client"].administrator and user_id != await virtool.samples.db.get_sample_owner(
                db, sample_id):
        return insufficient_rights("Must be administrator or sample owner")

    group = data.get("group", None)

    if group:
        existing_group_ids = await db.groups.distinct("_id") + ["none"]

        if group not in existing_group_ids:
            return bad_request("Group does not exist")

    # Update the sample document with the new rights.
    document = await db.samples.find_one_and_update(
        {"_id": sample_id}, {"$set": data},
        projection=virtool.samples.db.RIGHTS_PROJECTION)

    return json_response(document)
예제 #8
0
파일: api.py 프로젝트: jinxuchen/virtool
async def create_first(req):
    """
    Add a first user to the user database.

    """
    db = req.app["db"]
    data = await req.json()

    if await db.users.count():
        return conflict("Virtool already has at least one user")

    if data["user_id"] == "virtool":
        return bad_request("Reserved user name: virtool")

    error = await virtool.users.checks.check_password_length(req)

    if error:
        return bad_request(error)

    user_id = data["user_id"]

    await virtool.users.db.create(db,
                                  user_id,
                                  data["password"],
                                  force_reset=False)

    document = await virtool.users.db.edit(db, user_id, administrator=True)

    headers = {"Location": f"/api/users/{user_id}"}

    session, token = await virtool.users.sessions.create_session(
        db, virtool.http.auth.get_ip(req), user_id)

    req["client"].authorize(session, is_api=False)

    resp = json_response(virtool.utils.base_processor(
        {key: document[key]
         for key in virtool.users.db.PROJECTION}),
                         headers=headers,
                         status=201)

    resp.set_cookie("session_id", session["_id"])
    resp.set_cookie("session_token", token)

    return resp
예제 #9
0
파일: api.py 프로젝트: jinxuchen/virtool
async def edit(req):
    db = req.app["db"]
    data = await req.json()

    if "password" in data:
        error = await virtool.users.checks.check_password_length(req)

        if error:
            return bad_request(error)

    groups = await db.groups.distinct("_id")

    if "groups" in data:
        missing = [g for g in data["groups"] if g not in groups]

        if missing:
            return bad_request("Groups do not exist: " + ", ".join(missing))

    primary_group = data.get("primary_group", None)

    if primary_group and primary_group not in groups:
        return bad_request("Primary group does not exist")

    user_id = req.match_info["user_id"]

    if "administrator" in data and user_id == req["client"].user_id:
        return bad_request(
            "Users cannot modify their own administrative status")

    try:
        document = await virtool.users.db.edit(db, user_id, **data)
    except virtool.errors.DatabaseError as err:
        if "User does not exist" in str(err):
            return not_found("User does not exist")

        if "User is not member of group" in str(err):
            return conflict("User is not member of group")

        raise

    projected = virtool.db.utils.apply_projection(document,
                                                  virtool.users.db.PROJECTION)

    return json_response(virtool.utils.base_processor(projected))
예제 #10
0
파일: api.py 프로젝트: jinxuchen/virtool
async def analyze(req):
    """
    Starts an analysis job for a given sample.

    """
    db = req.app["db"]
    data = req["data"]

    sample_id = req.match_info["sample_id"]
    ref_id = data["ref_id"]

    try:
        if not await virtool.samples.db.check_rights(db, sample_id,
                                                     req["client"]):
            return insufficient_rights()
    except virtool.errors.DatabaseError as err:
        if "Sample does not exist" in str(err):
            return not_found()

        raise

    if not await db.references.count({"_id": ref_id}):
        return bad_request("Reference does not exist")

    if not await db.indexes.count({"reference.id": ref_id, "ready": True}):
        return bad_request("No ready index")

    # Generate a unique _id for the analysis entry
    document = await virtool.analyses.db.new(req.app, sample_id, ref_id,
                                             req["client"].user_id,
                                             data["algorithm"])

    document = virtool.utils.base_processor(document)

    sample = await virtool.samples.db.recalculate_algorithm_tags(db, sample_id)

    await req.app["dispatcher"].dispatch("samples", "update",
                                         virtool.utils.base_processor(sample))

    analysis_id = document["id"]

    return json_response(document,
                         status=201,
                         headers={"Location": f"/api/analyses/{analysis_id}"})
예제 #11
0
파일: auth.py 프로젝트: jinxuchen/virtool
async def authorize_with_api_key(req, handler):
    db = req.app["db"]

    authorization = req.headers.get("AUTHORIZATION")

    try:
        user_id, key = decode_authorization(authorization)
    except virtool.errors.AuthError:
        return bad_request("Malformed Authorization header")

    document = await db.keys.find_one({
        "_id": virtool.users.utils.hash_api_key(key),
        "user.id": user_id
    }, AUTHORIZATION_PROJECTION)

    if not document:
        return bad_request("Invalid Authorization header")

    req["client"].authorize(document, True)

    return await handler(req)
예제 #12
0
파일: api.py 프로젝트: jinxuchen/virtool
async def edit(req):
    """
    Edit the user account.

    """
    db = req.app["db"]
    data = req["data"]
    user_id = req["client"].user_id

    password = data.get("password")
    old_password = data.get("old_password")

    update = dict()

    if password is not None:
        error = await virtool.users.checks.check_password_length(req)

        if error:
            return bad_request(error)

        if not await virtool.users.db.validate_credentials(db, user_id, old_password or ""):
            return bad_request("Invalid credentials")

        update = virtool.account.db.compose_password_update(
            user_id,
            data["old_password"],
            password
        )

    if "email" in data:
        update["email"] = data["email"]

    document = await db.users.find_one_and_update({"_id": user_id}, {
        "$set": update
    }, projection=virtool.account.db.PROJECTION)

    return json_response(virtool.utils.base_processor(document))
예제 #13
0
파일: api.py 프로젝트: jinxuchen/virtool
async def update(req):
    app = req.app
    db = app["db"]

    ref_id = req.match_info["ref_id"]
    user_id = req["client"].user_id

    if not await virtool.db.utils.id_exists(db.references, ref_id):
        return not_found()

    if not await virtool.references.db.check_right(req, ref_id, "modify"):
        return insufficient_rights()

    release = await virtool.db.utils.get_one_field(db.references, "release",
                                                   ref_id)

    if release is None:
        return bad_request("Target release does not exist")

    created_at = virtool.utils.timestamp()

    context = {
        "created_at":
        created_at,
        "ref_id":
        ref_id,
        "release":
        await virtool.db.utils.get_one_field(db.references, "release", ref_id),
        "user_id":
        user_id
    }

    process = await virtool.processes.db.register(db,
                                                  "update_remote_reference",
                                                  context=context)

    release, update_subdocument = await virtool.references.db.update(
        req.app, created_at, process["id"], ref_id, release, user_id)

    p = virtool.references.db.UpdateRemoteReferenceProcess(
        req.app, process["id"])

    await aiojobs.aiohttp.spawn(req, p.run())

    return json_response(update_subdocument, status=201)
예제 #14
0
파일: api.py 프로젝트: jinxuchen/virtool
async def create(req):
    """
    Add a new otu to the collection. Checks to make sure the supplied otu name and abbreviation are not already in
    use in the collection. Any errors are sent back to the client.

    """
    db = req.app["db"]
    data = req["data"]

    ref_id = req.match_info["ref_id"]

    reference = await db.references.find_one(ref_id, ["groups", "users"])

    if reference is None:
        return not_found()

    if not await virtool.references.db.check_right(req, reference,
                                                   "modify_otu"):
        return insufficient_rights()

    name = data["name"].strip()
    abbreviation = data["abbreviation"].strip()

    # Check if either the name or abbreviation are already in use. Send a ``400`` if they are.
    message = await virtool.otus.db.check_name_and_abbreviation(
        db, ref_id, name, abbreviation)

    if message:
        return bad_request(message)

    joined = await virtool.otus.db.create(db, ref_id, name, abbreviation)

    description = virtool.history.utils.compose_create_description(joined)

    change = await virtool.history.db.add(db, "create", None, joined,
                                          description, req["client"].user_id)

    formatted = virtool.otus.utils.format_otu(joined,
                                              most_recent_change=change)

    headers = {"Location": "/api/otus/" + formatted["id"]}

    return json_response(formatted, status=201, headers=headers)
예제 #15
0
파일: api.py 프로젝트: jinxuchen/virtool
async def login(req):
    """
    Create a new session for the user with `username`.

    """
    db = req.app["db"]
    data = req["data"]

    user_id = data["username"]
    password = data["password"]

    # When this value is set, the session will last for 1 month instead of the 1 hour default.
    remember = data["remember"]

    # Re-render the login page with an error message if the username and/or password are invalid.
    if not await virtool.users.db.validate_credentials(db, user_id, password):
        return bad_request("Invalid username or password")

    session_id = req.cookies.get("session_id")

    # If the user's password needs to be reset, redirect to the reset page without authorizing the session. A one-time
    # reset code is generated and added to the query string.
    if await virtool.db.utils.get_one_field(db.users, "force_reset", user_id):
        return json_response({
            "reset": True,
            "reset_code": await virtool.users.sessions.create_reset_code(db, session_id, user_id, remember)
        }, status=200)

    session, token = await virtool.users.sessions.replace_session(
        db,
        session_id,
        virtool.http.auth.get_ip(req),
        user_id,
        remember
    )

    resp = json_response({"reset": False}, status=201)

    resp.set_cookie("session_id", session["_id"], httponly=True)
    resp.set_cookie("session_token", token, httponly=True)

    return resp
예제 #16
0
파일: api.py 프로젝트: jinxuchen/virtool
async def remove(req):
    """
    Remove a user.

    """
    db = req.app["db"]

    user_id = req.match_info["user_id"]

    if user_id == req["client"].user_id:
        return bad_request("Cannot remove own account")

    delete_result = await db.users.delete_one({"_id": user_id})

    # Remove user from all references.
    await db.references.update_many({}, {"$pull": {"users": {"id": user_id}}})

    if delete_result.deleted_count == 0:
        return not_found()

    return no_content()
예제 #17
0
async def create(req):
    """
    Adds a new user group.

    """
    db, data = req.app["db"], req["data"]

    document = {
        "_id": data["group_id"].lower(),
        "permissions": virtool.users.utils.generate_base_permissions()
    }

    try:
        await db.groups.insert_one(document)
    except pymongo.errors.DuplicateKeyError:
        return bad_request("Group already exists")

    headers = {"Location": "/api/groups/" + data["group_id"]}

    return json_response(virtool.utils.base_processor(document),
                         status=201,
                         headers=headers)
예제 #18
0
파일: api.py 프로젝트: jinxuchen/virtool
async def remove(req):
    """
    Remove an analysis document by its id.

    """
    db = req.app["db"]

    analysis_id = req.match_info["analysis_id"]

    document = await db.analyses.find_one({"_id": analysis_id}, ["job", "ready", "sample"])

    if not document:
        return not_found()

    sample_id = document["sample"]["id"]

    sample = await db.samples.find_one({"_id": sample_id}, virtool.samples.db.PROJECTION)

    if not sample:
        return bad_request("Parent sample does not exist")

    read, write = virtool.samples.utils.get_sample_rights(sample, req["client"])

    if not read or not write:
        return insufficient_rights()

    if not document["ready"]:
        return conflict("Analysis is still running")

    await db.analyses.delete_one({"_id": analysis_id})

    path = os.path.join(req.app["settings"]["data_path"], "samples", sample_id, "analysis", analysis_id)

    await req.app["run_in_thread"](virtool.utils.rm, path, True)

    await virtool.samples.db.recalculate_algorithm_tags(db, sample_id)

    return no_content()
예제 #19
0
파일: api.py 프로젝트: jinxuchen/virtool
async def create(req):
    db = req.app["db"]
    data = req["data"]
    user_id = req["client"].user_id
    settings = req.app["settings"]

    name_error_message = await virtool.samples.db.check_name(
        db, req.app["settings"], data["name"])

    if name_error_message:
        return bad_request(name_error_message)

    # Make sure a subtraction host was submitted and it exists.
    if not await db.subtraction.count({
            "_id": data["subtraction"],
            "is_host": True
    }):
        return bad_request("Subtraction does not exist")

    # Make sure all of the passed file ids exist.
    if not await virtool.db.utils.ids_exist(db.files, data["files"]):
        return bad_request("File does not exist")

    sample_id = await virtool.db.utils.get_new_id(db.samples)

    document = deepcopy(data)

    sample_group_setting = settings["sample_group"]

    # Require a valid ``group`` field if the ``sample_group`` setting is ``users_primary_group``.
    if sample_group_setting == "force_choice":
        force_choice_error_message = await virtool.samples.db.validate_force_choice_group(
            db, data)

        if force_choice_error_message:
            if "not found" in force_choice_error_message:
                return bad_request(force_choice_error_message)

            return bad_request(force_choice_error_message)

    # Assign the user"s primary group as the sample owner group if the setting is ``users_primary_group``.
    elif sample_group_setting == "users_primary_group":
        document["group"] = await virtool.db.utils.get_one_field(
            db.users, "primary_group", user_id)

    # Make the owner group none if the setting is none.
    elif sample_group_setting == "none":
        document["group"] = "none"

    document.update({
        "_id": sample_id,
        "nuvs": False,
        "pathoscope": False,
        "created_at": virtool.utils.timestamp(),
        "format": "fastq",
        "imported": "ip",
        "quality": None,
        "analyzed": False,
        "hold": True,
        "archived": False,
        "group_read": settings["sample_group_read"],
        "group_write": settings["sample_group_write"],
        "all_read": settings["sample_all_read"],
        "all_write": settings["sample_all_write"],
        "srna": data["srna"],
        "subtraction": {
            "id": data["subtraction"]
        },
        "user": {
            "id": user_id
        },
        "paired": len(data["files"]) == 2
    })

    files = [
        await db.files.find_one(file_id, ["_id", "name", "size"])
        for file_id in data["files"]
    ]

    files = [virtool.utils.base_processor(file) for file in files]

    document["files"] = files

    await db.samples.insert_one(document)

    await virtool.files.db.reserve(db, data["files"])

    task_args = {"sample_id": sample_id, "files": files, "srna": data["srna"]}

    # Create job document.
    job = await virtool.jobs.db.create(db, req.app["settings"],
                                       "create_sample", task_args, user_id)

    await req.app["jobs"].enqueue(job["_id"])

    headers = {"Location": "/api/samples/" + sample_id}

    return json_response(virtool.utils.base_processor(document),
                         status=201,
                         headers=headers)
예제 #20
0
파일: api.py 프로젝트: jinxuchen/virtool
async def create(req):
    db = req.app["db"]
    data = req["data"]
    settings = req.app["settings"]

    user_id = req["client"].user_id

    clone_from = data.get("clone_from", None)
    import_from = data.get("import_from", None)
    remote_from = data.get("remote_from", None)
    release_id = data.get("release_id", None) or 11447367

    if clone_from:

        if not await db.references.count({"_id": clone_from}):
            return bad_request("Source reference does not exist")

        manifest = await virtool.references.db.get_manifest(db, clone_from)

        document = await virtool.references.db.create_clone(
            db, settings, data["name"], clone_from, data["description"],
            user_id)

        context = {
            "created_at": document["created_at"],
            "manifest": manifest,
            "ref_id": document["_id"],
            "user_id": user_id
        }

        process = await virtool.processes.db.register(db,
                                                      "clone_reference",
                                                      context=context)

        p = virtool.references.db.CloneReferenceProcess(req.app, process["id"])

        document["process"] = {"id": p.id}

        await aiojobs.aiohttp.spawn(req, p.run())

    elif import_from:
        if not await db.files.count({"_id": import_from}):
            return not_found("File not found")

        path = os.path.join(req.app["settings"]["data_path"], "files",
                            import_from)

        document = await virtool.references.db.create_import(
            db, settings, data["name"], data["description"], import_from,
            user_id)

        context = {
            "created_at": document["created_at"],
            "path": path,
            "ref_id": document["_id"],
            "user_id": user_id
        }

        process = await virtool.processes.db.register(db,
                                                      "import_reference",
                                                      context=context)

        p = virtool.references.db.ImportReferenceProcess(
            req.app, process["id"])

        document["process"] = {"id": p.id}

        await aiojobs.aiohttp.spawn(req, p.run())

    elif remote_from:
        try:
            release = await virtool.github.get_release(settings,
                                                       req.app["client"],
                                                       remote_from,
                                                       release_id=release_id)

        except aiohttp.ClientConnectionError:
            return bad_gateway("Could not reach GitHub")

        except virtool.errors.GitHubError as err:
            if "404" in str(err):
                return bad_gateway("Could not retrieve latest GitHub release")

            raise

        release = virtool.github.format_release(release)

        document = await virtool.references.db.create_remote(
            db, settings, release, remote_from, user_id)

        process = await virtool.processes.db.register(db, "remote_reference")

        document["process"] = {"id": process["id"]}

        await aiojobs.aiohttp.spawn(
            req,
            virtool.references.db.finish_remote(req.app, release,
                                                document["_id"],
                                                document["created_at"],
                                                process["id"], user_id))

    else:
        document = await virtool.references.db.create_document(
            db,
            settings,
            data["name"],
            data["organism"],
            data["description"],
            data["data_type"],
            user_id=req["client"].user_id)

    await db.references.insert_one(document)

    headers = {"Location": "/api/refs/" + document["_id"]}

    document.update(await
                    virtool.references.db.get_computed(db, document["_id"],
                                                       None))

    return json_response(virtool.utils.base_processor(document),
                         headers=headers,
                         status=201)
예제 #21
0
파일: api.py 프로젝트: jinxuchen/virtool
async def blast(req):
    """
    BLAST a contig sequence that is part of a NuVs result record. The resulting BLAST data will be attached to that
    sequence.

    """
    db = req.app["db"]
    settings = req.app["settings"]

    analysis_id = req.match_info["analysis_id"]
    sequence_index = int(req.match_info["sequence_index"])

    document = await db.analyses.find_one({"_id": analysis_id}, ["ready", "algorithm", "results", "sample"])

    if not document:
        return not_found("Analysis not found")

    if document["algorithm"] != "nuvs":
        return conflict("Not a NuVs analysis")

    if not document["ready"]:
        return conflict("Analysis is still running")

    sequence = virtool.analyses.utils.find_nuvs_sequence_by_index(document, sequence_index)

    if sequence is None:
        return not_found("Sequence not found")

    sample = await db.samples.find_one({"_id": document["sample"]["id"]}, virtool.samples.db.PROJECTION)

    if not sample:
        return bad_request("Parent sample does not exist")

    _, write = virtool.samples.utils.get_sample_rights(sample, req["client"])

    if not write:
        return insufficient_rights()

    # Start a BLAST at NCBI with the specified sequence. Return a RID that identifies the BLAST run.
    rid, _ = await virtool.bio.initialize_ncbi_blast(req.app["settings"], sequence)

    blast_data, document = await virtool.analyses.db.update_nuvs_blast(
        db,
        settings,
        analysis_id,
        sequence_index,
        rid
    )

    # Wait on BLAST request as a Task until the it completes on NCBI. At that point the sequence in the DB will be
    # updated with the BLAST result.
    await aiojobs.aiohttp.spawn(req, virtool.bio.wait_for_blast_result(
        db,
        req.app["settings"],
        analysis_id,
        sequence_index,
        rid
    ))

    headers = {
        "Location": f"/api/analyses/{analysis_id}/{sequence_index}/blast"
    }

    return json_response(blast_data, headers=headers, status=201)
예제 #22
0
파일: api.py 프로젝트: jinxuchen/virtool
async def edit(req):
    """
    Edit an existing OTU. Checks to make sure the supplied OTU name and abbreviation are not already in use in
    the collection.

    """
    db = req.app["db"]
    data = req["data"]

    otu_id = req.match_info["otu_id"]

    # Get existing complete otu record, at the same time ensuring it exists. Send a ``404`` if not.
    old = await virtool.otus.db.join(db, otu_id)

    if not old:
        return not_found()

    ref_id = old["reference"]["id"]

    if not await virtool.references.db.check_right(req, ref_id, "modify_otu"):
        return insufficient_rights()

    name, abbreviation, schema = virtool.otus.utils.evaluate_changes(data, old)

    # Send ``200`` with the existing otu record if no change will be made.
    if name is None and abbreviation is None and schema is None:
        return json_response(await virtool.otus.db.join_and_format(db, otu_id))

    # Make sure new name or abbreviation are not already in use.
    message = await virtool.otus.db.check_name_and_abbreviation(
        db, ref_id, name, abbreviation)

    if message:
        return bad_request(message)

    # Update the ``modified`` and ``verified`` fields in the otu document now, because we are definitely going to
    # modify the otu.
    data["verified"] = False

    # If the name is changing, update the ``lower_name`` field in the otu document.
    if name:
        data["lower_name"] = name.lower()

    # Update the database collection.
    document = await db.otus.find_one_and_update({"_id": otu_id}, {
        "$set": data,
        "$inc": {
            "version": 1
        }
    })

    await virtool.otus.db.update_sequence_segments(db, old, document)

    new = await virtool.otus.db.join(db, otu_id, document)

    issues = await virtool.otus.db.update_verification(db, new)

    description = virtool.history.utils.compose_edit_description(
        name, abbreviation, old["abbreviation"], schema)

    await virtool.history.db.add(db, "edit", old, new, description,
                                 req["client"].user_id)

    return json_response(await virtool.otus.db.join_and_format(db,
                                                               otu_id,
                                                               joined=new,
                                                               issues=issues))
예제 #23
0
파일: api.py 프로젝트: jinxuchen/virtool
async def create(req):
    """
    Starts a job to rebuild the otus Bowtie2 index on disk. Does a check to make sure there are no unverified
    otus in the collection and updates otu history to show the version and id of the new index.

    """
    db = req.app["db"]

    ref_id = req.match_info["ref_id"]

    reference = await db.references.find_one(ref_id, ["groups", "users"])

    if reference is None:
        return not_found()

    if not await virtool.references.db.check_right(req, reference, "build"):
        return insufficient_rights()

    if await db.indexes.count({"reference.id": ref_id, "ready": False}):
        return conflict("Index build already in progress")

    if await db.otus.count({"reference.id": ref_id, "verified": False}):
        return bad_request("There are unverified OTUs")

    if not await db.history.count({
            "reference.id": ref_id,
            "index.id": "unbuilt"
    }):
        return bad_request("There are no unbuilt changes")

    index_id = await virtool.db.utils.get_new_id(db.indexes)

    index_version = await virtool.indexes.db.get_next_version(db, ref_id)

    job_id = await virtool.db.utils.get_new_id(db.jobs)

    manifest = await virtool.references.db.get_manifest(db, ref_id)

    user_id = req["client"].user_id

    document = {
        "_id": index_id,
        "version": index_version,
        "created_at": virtool.utils.timestamp(),
        "manifest": manifest,
        "ready": False,
        "has_files": True,
        "job": {
            "id": job_id
        },
        "reference": {
            "id": ref_id
        },
        "user": {
            "id": user_id
        }
    }

    await db.indexes.insert_one(document)

    await db.history.update_many(
        {
            "index.id": "unbuilt",
            "reference.id": ref_id
        }, {"$set": {
            "index": {
                "id": index_id,
                "version": index_version
            }
        }})

    # A dict of task_args for the rebuild job.
    task_args = {
        "ref_id": ref_id,
        "user_id": user_id,
        "index_id": index_id,
        "index_version": index_version,
        "manifest": manifest
    }

    # Create job document.
    job = await virtool.jobs.db.create(db,
                                       req.app["settings"],
                                       "build_index",
                                       task_args,
                                       user_id,
                                       job_id=job_id)

    await req.app["jobs"].enqueue(job["_id"])

    headers = {"Location": "/api/indexes/" + index_id}

    return json_response(virtool.utils.base_processor(document),
                         status=201,
                         headers=headers)
예제 #24
0
파일: api.py 프로젝트: jinxuchen/virtool
async def add_isolate(req):
    """
    Add a new isolate to a otu.

    """
    db = req.app["db"]
    data = req["data"]

    otu_id = req.match_info["otu_id"]

    document = await db.otus.find_one(otu_id)

    if not document:
        return not_found()

    if not await virtool.references.db.check_right(
            req, document["reference"]["id"], "modify_otu"):
        return insufficient_rights()

    isolates = deepcopy(document["isolates"])

    # True if the new isolate should be default and any existing isolates should be non-default.
    will_be_default = not isolates or data["default"]

    # Get the complete, joined entry before the update.
    old = await virtool.otus.db.join(db, otu_id, document)

    # All source types are stored in lower case.
    data["source_type"] = data["source_type"].lower()

    if not await virtool.references.db.check_source_type(
            db, document["reference"]["id"], data["source_type"]):
        return bad_request("Source type is not allowed")

    # Set ``default`` to ``False`` for all existing isolates if the new one should be default.
    if isolates and data["default"]:
        for isolate in isolates:
            isolate["default"] = False

    # Force the new isolate as default if it is the first isolate.
    if not isolates:
        data["default"] = True

    # Set the isolate as the default isolate if it is the first one.
    data["default"] = will_be_default

    isolate_id = await virtool.otus.db.append_isolate(db, otu_id, isolates,
                                                      data)

    # Get the joined entry now that it has been updated.
    new = await virtool.otus.db.join(db, otu_id)

    await virtool.otus.db.update_verification(db, new)

    isolate_name = virtool.otus.utils.format_isolate_name(data)

    description = f"Added {isolate_name}"

    if will_be_default:
        description += " as default"

    await virtool.history.db.add(db, "add_isolate", old, new, description,
                                 req["client"].user_id)

    headers = {"Location": f"/api/otus/{otu_id}/isolates/{isolate_id}"}

    return json_response(dict(data, id=isolate_id, sequences=[]),
                         status=201,
                         headers=headers)
예제 #25
0
파일: api.py 프로젝트: jinxuchen/virtool
async def edit_isolate(req):
    """
    Edit an existing isolate.

    """
    db = req.app["db"]
    data = req["data"]

    otu_id = req.match_info["otu_id"]
    isolate_id = req.match_info["isolate_id"]

    document = await db.otus.find_one({
        "_id": otu_id,
        "isolates.id": isolate_id
    })

    if not document:
        return not_found()

    ref_id = document["reference"]["id"]

    if not await virtool.references.db.check_right(req, ref_id, "modify_otu"):
        return insufficient_rights()

    reference = await db.references.find_one(
        ref_id, ["restrict_source_types", "source_types"])

    isolates = deepcopy(document["isolates"])

    isolate = virtool.otus.utils.find_isolate(isolates, isolate_id)

    # All source types are stored in lower case.
    if "source_type" in data:
        data["source_type"] = data["source_type"].lower()

        if reference["restrict_source_types"] and data[
                "source_type"] not in reference["source_types"]:
            return bad_request("Source type is not allowed")

    old_isolate_name = virtool.otus.utils.format_isolate_name(isolate)

    isolate.update(data)

    old = await virtool.otus.db.join(db, otu_id)

    # Replace the isolates list with the update one.
    document = await db.otus.find_one_and_update({"_id": otu_id}, {
        "$set": {
            "isolates": isolates,
            "verified": False
        },
        "$inc": {
            "version": 1
        }
    })

    # Get the joined entry now that it has been updated.
    new = await virtool.otus.db.join(db, otu_id, document)

    await virtool.otus.db.update_verification(db, new)

    isolate_name = virtool.otus.utils.format_isolate_name(isolate)

    # Use the old and new entry to add a new history document for the change.
    await virtool.history.db.add(
        db, "edit_isolate", old, new,
        f"Renamed {old_isolate_name} to {isolate_name}", req["client"].user_id)

    complete = await virtool.otus.db.join_and_format(db, otu_id, joined=new)

    for isolate in complete["isolates"]:
        if isolate["id"] == isolate_id:
            return json_response(isolate, status=200)
예제 #26
0
파일: api.py 프로젝트: jinxuchen/virtool
async def create_sequence(req):
    """
    Create a new sequence record for the given isolate.

    """
    db, data = req.app["db"], req["data"]

    # Extract variables from URL path.
    otu_id, isolate_id = (req.match_info[key]
                          for key in ["otu_id", "isolate_id"])

    # Get the subject otu document. Will be ``None`` if it doesn't exist. This will result in a ``404`` response.
    document = await db.otus.find_one({
        "_id": otu_id,
        "isolates.id": isolate_id
    })

    if not document:
        return not_found()

    ref_id = document["reference"]["id"]

    if not await virtool.references.db.check_right(req, ref_id, "modify_otu"):
        return insufficient_rights()

    segment = data.get("segment", None)

    if segment and segment not in {
            s["name"]
            for s in document.get("schema", {})
    }:
        return bad_request("Segment does not exist")

    # Update POST data to make sequence document.
    data.update({
        "otu_id": otu_id,
        "isolate_id": isolate_id,
        "host": data.get("host", ""),
        "reference": {
            "id": ref_id
        },
        "segment": segment,
        "sequence": data["sequence"].replace(" ", "").replace("\n", "")
    })

    old = await virtool.otus.db.join(db, otu_id, document)

    sequence_document = await db.sequences.insert_one(data)

    new = await db.otus.find_one_and_update({"_id": otu_id}, {
        "$set": {
            "verified": False
        },
        "$inc": {
            "version": 1
        }
    })

    new = await virtool.otus.db.join(db, otu_id, new)

    await virtool.otus.db.update_verification(db, new)

    isolate = virtool.otus.utils.find_isolate(old["isolates"], isolate_id)

    await virtool.history.db.add(
        db, "create_sequence", old, new,
        f"Created new sequence {data['accession']} in {virtool.otus.utils.format_isolate_name(isolate)}",
        req["client"].user_id)

    headers = {
        "Location":
        f"/api/otus/{otu_id}/isolates/{isolate_id}/sequences/{sequence_document['_id']}"
    }

    return json_response(virtool.utils.base_processor(data),
                         status=201,
                         headers=headers)