示例#1
0
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", "workflow", "results", "sample"])

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

    if document["workflow"] != "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(req.app, analysis_id, sequence_index,
                                          rid))

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

    return json_response(blast_data, headers=headers, status=201)
示例#2
0
async def edit_user(req):
    db = req.app["db"]
    data = req["data"]
    ref_id = req.match_info["ref_id"]
    user_id = req.match_info["user_id"]

    document = await db.references.find_one(
        {
            "_id": ref_id,
            "users.id": user_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()

    subdocument = await virtool.references.db.edit_group_or_user(
        db, ref_id, user_id, "users", data)

    if subdocument is None:
        return not_found()

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

    return json_response(subdocument)
示例#3
0
async def remove_sequence(req):
    """
    Remove a sequence from an isolate.

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

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

    if not await db.sequences.count_documents({"_id": sequence_id}):
        return not_found()

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

    if document is None:
        return not_found()

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

    await asyncio.shield(virtool.otus.sequences.remove(
        req.app,
        otu_id,
        isolate_id,
        sequence_id,
        req["client"].user_id
    ))

    return no_content()
示例#4
0
async def replace_sample_file(req):
    error_resp = naive_validator(req)

    if error_resp:
        return error_resp

    db = req.app["db"]

    filename = req.query["name"]

    sample_id = req.match_info["sample_id"]
    suffix = req.match_info["suffix"]

    index = int(suffix) - 1

    minimal = await db.samples.find_one(sample_id, ["paired"])

    if minimal is None:
        return not_found("Sample not found")

    if suffix != "1" and suffix != "2":
        return not_found("Invalid file suffix. Must be 1 or 2.")

    if suffix == "2" and not minimal.get("paired"):
        return not_found("Sample is not paired")

    document = await virtool.files.db.create(
        db,
        filename,
        "sample_replacement",
        user_id=req["client"].user_id,
        reserved=True
    )

    await naive_writer(req, document["id"])

    replacement = {
        "id": document["id"],
        "name": document["name"],
        "uploaded_at": document["uploaded_at"]
    }

    files = await virtool.db.utils.get_one_field(db.samples, "files", sample_id)

    files[index].update({
        "replacement": replacement
    })

    await db.samples.find_one_and_update({"_id": sample_id}, {
        "$set": {
            "files": files
        }
    })

    await virtool.samples.db.attempt_file_replacement(req.app, sample_id, req["client"].user_id)

    return json_response(document, status=201)
示例#5
0
async def remove(req):
    """
    Remove a job.

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

    job_id = req.match_info["job_id"]

    document = await db.jobs.find_one(job_id)

    if not document:
        return not_found()

    if virtool.jobs.is_running_or_waiting(document):
        return conflict("Job is running or waiting and cannot be removed")

    # Removed the documents associated with the job ids from the database.
    await db.jobs.delete_one({"_id": job_id})

    try:
        # Calculate the log path and remove the log file. If it exists, return True.
        path = os.path.join(req.app["settings"]["data_path"], "logs", "jobs", job_id + ".log")
        await req.app["run_in_thread"](virtool.utils.rm, path)
    except OSError:
        pass

    return no_content()
示例#6
0
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()

    await virtool.subtractions.db.attach_subtraction(db, document)

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

    return json_response(virtool.utils.base_processor(document))
示例#7
0
async def install(req):
    db = req.app["db"]

    releases = await virtool.db.utils.get_one_field(db.status, "releases",
                                                    "software")

    try:
        latest_release = releases[0]
    except IndexError:
        return not_found("Could not find latest uninstalled release")

    process = await virtool.processes.db.register(
        db, "update_software", context={"file_size": latest_release["size"]})

    await db.status.update_one(
        {"_id": "software"}, {"$set": {
            "process": process,
            "updating": True
        }})

    update = virtool.github.create_update_subdocument(
        latest_release, False, req["client"].user_id,
        virtool.utils.timestamp())

    await aiojobs.aiohttp.spawn(
        req, virtool.software.db.install(req.app, latest_release,
                                         process["id"]))

    return json_response(update)
示例#8
0
async def get(req):
    """
    Get the complete document for a given index.

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

    index_id = req.match_info["index_id"]

    document = await db.indexes.find_one(index_id)

    if not document:
        return not_found()

    contributors, otus = await asyncio.gather(
        virtool.indexes.db.get_contributors(db, index_id),
        virtool.indexes.db.get_otus(db, index_id)
    )

    document.update({
        "change_count": sum(v["change_count"] for v in otus),
        "contributors": contributors,
        "otus": otus,
    })

    document = await virtool.indexes.db.processor(db, document)

    return json_response(document)
示例#9
0
async def set_as_default(req):
    """
    Set an isolate as default.

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

    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}, ["reference"])

    if not document:
        return not_found()

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

    isolate = await asyncio.shield(virtool.otus.isolates.set_default(
        req.app,
        otu_id,
        isolate_id,
        req["client"].user_id
    ))

    return json_response(isolate)
示例#10
0
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_documents({"_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")

    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)
示例#11
0
async def remove(req):
    """
    Remove a reference and its otus, history, and indexes.

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

    ref_id = req.match_info["ref_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, "remove"):
        return insufficient_rights()

    user_id = req["client"].user_id

    context = {"ref_id": ref_id, "user_id": user_id}

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

    await db.references.delete_one({"_id": ref_id})

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

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

    headers = {"Content-Location": f"/api/processes/{process['id']}"}

    return json_response(process, 202, headers)
示例#12
0
async def upload(req):
    db = req.app["db"]

    file_type = req.match_info["file_type"]

    if file_type not in FILE_TYPES:
        return not_found()

    errors = naive_validator(req)

    if errors:
        return invalid_query(errors)

    filename = req.query["name"]

    document = await virtool.files.db.create(
        db,
        filename,
        file_type,
        user_id=req["client"].user_id
    )

    file_id = document["id"]

    await naive_writer(req, file_id)

    await virtool.uploads.db.finish_upload(req.app, file_id)

    headers = {
        "Location": f"/api/files/{file_id}"
    }

    return json_response(document, status=201, headers=headers)
示例#13
0
async def remove(req):
    """
    Remove a sample document and all associated analyses.

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

    sample_id = req.match_info["sample_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

    await virtool.samples.db.remove_samples(
        db,
        req.app["settings"],
        [sample_id]
    )

    return no_content()
示例#14
0
async def get_release(req):
    """
    Get the latest update from GitHub and return it. Also updates the reference document. This is the only way of doing
    so without waiting for an automatic refresh every 10 minutes.

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

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

    if not await db.references.count_documents({
            "_id": ref_id,
            "remotes_from": {
                "$exists": True
            }
    }):
        return bad_request("Not a remote reference")

    try:
        release = await virtool.references.db.fetch_and_update_release(
            req.app, ref_id)
    except aiohttp.ClientConnectorError:
        return bad_gateway("Could not reach GitHub")

    if release is None:
        return bad_gateway("Release repository does not exist on GitHub")

    return json_response(release)
示例#15
0
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)
示例#16
0
async def edit(req):
    """
    Updates the nickname for an existing subtraction.

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

    subtraction_id = req.match_info["subtraction_id"]

    update = dict()

    try:
        update["name"] = data["name"]
    except KeyError:
        pass

    try:
        update["nickname"] = data["nickname"]
    except KeyError:
        pass

    document = await db.subtraction.find_one_and_update({"_id": subtraction_id}, {
        "$set": update
    })

    if document is None:
        return not_found()

    document["linked_samples"] = await virtool.subtractions.db.get_linked_samples(db, subtraction_id)

    return json_response(virtool.utils.base_processor(document))
示例#17
0
async def find_history(req):
    """
    Find history changes for a specific index.

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

    index_id = req.match_info["index_id"]

    if not await db.indexes.count_documents({"_id": index_id}):
        return not_found()

    term = req.query.get("term")

    db_query = {
        "index.id": index_id
    }

    if term:
        db_query.update(virtool.api.utils.compose_regex_query(term, ["otu.name", "user.id"]))

    data = await virtool.api.utils.paginate(
        db.history,
        db_query,
        req.query,
        sort=[("otu.name", 1), ("otu.version", -1)],
        projection=virtool.history.db.LIST_PROJECTION,
        reverse=True
    )

    return json_response(data)
示例#18
0
async def update_permissions(req):
    """
    Updates the permissions of a given group.

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

    group_id = req.match_info["group_id"]

    old_document = await db.groups.find_one({"_id": group_id}, ["permissions"])

    if not old_document:
        return not_found()

    old_document["permissions"].update(data["permissions"])

    # Get the current permissions dict for the passed group id.
    document = await db.groups.find_one_and_update({"_id": group_id}, {
        "$set": {
            "permissions": old_document["permissions"]
        }
    })

    await virtool.groups.db.update_member_users(db, group_id)

    return json_response(virtool.utils.base_processor(document))
示例#19
0
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_documents({"_id": ref_id}):
        return bad_request("Reference does not exist")

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

    subtraction_id = data.get("subtraction_id")

    if subtraction_id is None:
        subtraction = await virtool.db.utils.get_one_field(db.samples, "subtraction", sample_id)
        subtraction_id = subtraction["id"]

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

    document = virtool.utils.base_processor(document)

    sample = await virtool.samples.db.recalculate_workflow_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}"
        }
    )
示例#20
0
async def find_indexes(req):
    db = req.app["db"]

    ref_id = req.match_info["ref_id"]

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

    data = await virtool.indexes.db.find(db, req.query, ref_id=ref_id)

    return json_response(data)
示例#21
0
async def list_history(req):
    db = req.app["db"]

    otu_id = req.match_info["otu_id"]

    if not await db.otus.count_documents({"_id": otu_id}):
        return not_found()

    cursor = db.history.find({"otu.id": otu_id}, projection=virtool.history.db.LIST_PROJECTION)

    return json_response([d async for d in cursor])
示例#22
0
async def list_groups(req):
    db = req.app["db"]
    ref_id = req.match_info["ref_id"]

    if not await db.references.count_documents({"_id": ref_id}):
        return not_found()

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

    return json_response(groups)
示例#23
0
async def get(req):
    """
    Gets a complete group document.

    """
    document = await req.app["db"].groups.find_one(req.match_info["group_id"])

    if document:
        return json_response(virtool.utils.base_processor(document))

    return not_found()
示例#24
0
async def get(req):
    db = req.app["db"]

    process_id = req.match_info["process_id"]

    document = await db.processes.find_one(process_id)

    if not document:
        return not_found()

    return json_response(virtool.utils.base_processor(document))
示例#25
0
async def get(req):
    """
    Get a near-complete user document. Password data are removed.

    """
    document = await req.app["db"].users.find_one(req.match_info["user_id"],
                                                  virtool.users.db.PROJECTION)

    if not document:
        return not_found()

    return json_response(virtool.utils.base_processor(document))
示例#26
0
async def remove(req):
    file_id = req.match_info["file_id"]

    deleted_count = await virtool.files.db.remove(req.app["db"],
                                                  req.app["settings"],
                                                  req.app["run_in_thread"],
                                                  file_id)

    if deleted_count == 0:
        return not_found()

    return json_response({"file_id": file_id, "removed": True})
示例#27
0
async def remove(req):
    db = req.app["db"]
    settings = req.app["settings"]

    subtraction_id = req.match_info["subtraction_id"]

    updated_count = await asyncio.shield(virtool.subtractions.db.delete(req.app, subtraction_id))

    if updated_count == 0:
        return not_found()

    return no_content()
示例#28
0
async def get(req):
    """
    Get a complete individual HMM annotation document.

    """
    document = await req.app["db"].hmm.find_one(
        {"_id": req.match_info["hmm_id"]})

    if document is None:
        return not_found()

    return json_response(virtool.utils.base_processor(document))
示例#29
0
async def get(req):
    """
    Get a specific change document by its ``change_id``.

    """
    change_id = req.match_info["change_id"]

    document = await virtool.history.db.get(req.app, change_id)

    if document:
        return json_response(document)

    return not_found()
示例#30
0
async def get(req):
    """
    Return the complete document for a given job.

    """
    job_id = req.match_info["job_id"]

    document = await req.app["db"].jobs.find_one(job_id)

    if not document:
        return not_found()

    return json_response(virtool.utils.base_processor(document))