Пример #1
0
async def download_sequence(req):
    """
    Download a FASTA file containing a single Virtool sequence.

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

    sequence_id = req.match_info["sequence_id"]

    try:
        filename, fasta = await virtool.db.downloads.generate_sequence_fasta(
            db, sequence_id)
    except virtool.errors.DatabaseError as err:
        if "Sequence does not exist" in str(err):
            return not_found("Sequence not found")

        if "Isolate does not exist" in str(err):
            return not_found("Isolate not found")

        if "OTU does not exist" in str(err):
            return not_found("OTU not found")

        raise

    if fasta is None:
        return web.Response(status=404)

    return web.Response(
        text=fasta,
        headers={"Content-Disposition": f"attachment; filename={filename}"})
Пример #2
0
async def download_otu(req):
    """
    Download a FASTA file containing the sequences for all isolates in a single Virtool otu.

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

    otu_id = req.match_info["otu_id"]

    try:
        filename, fasta = await virtool.db.downloads.generate_otu_fasta(
            db, otu_id)
    except virtool.errors.DatabaseError as err:
        if "Sequence does not exist" in str(err):
            return not_found("Sequence not found")

        if "OTU does not exist" in str(err):
            return not_found("OTU not found")

        raise

    if not fasta:
        return web.Response(status=404)

    return web.Response(text=fasta,
                        headers={
                            "Content-Disposition":
                            "attachment; filename='{}'".format(filename)
                        })
Пример #3
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.db.references.check_right(req, ref_id, "modify"):
        return insufficient_rights()

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

    if subdocument is None:
        return not_found()

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

    return json_response(subdocument)
Пример #4
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", "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.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.db.samples.PROJECTION)

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

    _, write = virtool.samples.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.db.analyses.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)
Пример #5
0
async def download_sample_reads(req):
    db = req.app["db"]
    data_path = req.app["settings"]["data_path"]

    sample_id = req.match_info["sample_id"]

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

    if document is None or not document.get("files", False):
        return not_found()

    suffix = req.match_info["suffix"]

    path = os.path.join(data_path, "samples", sample_id,
                        f"reads_{suffix}.fq.gz")

    if not os.path.isfile(path):
        return not_found()

    file_stats = virtool.utils.file_stats(path)

    headers = {
        "Content-Length": file_stats["size"],
        "Content-Type": "application/gzip"
    }

    return web.FileResponse(path, chunk_size=1024 * 1024, headers=headers)
Пример #6
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.db.files.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.db.samples.attempt_file_replacement(req.app, sample_id,
                                                      req["client"].user_id)

    return json_response(document, status=201)
Пример #7
0
async def edit_sequence(req):
    db, data = req.app["db"], req["data"]

    otu_id, isolate_id, sequence_id = (
        req.match_info[key] for key in ["otu_id", "isolate_id", "sequence_id"])

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

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

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

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

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

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

    data["sequence"] = data["sequence"].replace(" ", "").replace("\n", "")

    updated_sequence = await db.sequences.find_one_and_update(
        {"_id": sequence_id}, {"$set": data})

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

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

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

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

    await virtool.db.history.add(
        db, "edit_sequence", old, new,
        f"Edited sequence {sequence_id} in {virtool.otus.format_isolate_name(isolate)}",
        req["client"].user_id)

    return json_response(virtool.utils.base_processor(updated_sequence))
Пример #8
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({"_id": sequence_id}):
        return not_found()

    old = await virtool.db.otus.join(db, {
        "_id": otu_id,
        "isolates.id": isolate_id
    })

    if old is None:
        return not_found()

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

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

    await db.sequences.delete_one({"_id": sequence_id})

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

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

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

    isolate_name = virtool.otus.format_isolate_name(isolate)

    await virtool.db.history.add(
        db, "remove_sequence", old, new,
        f"Removed sequence {sequence_id} from {isolate_name}",
        req["client"].user_id)

    return no_content()
Пример #9
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.db.files.create(db,
                                             filename,
                                             file_type,
                                             user_id=req["client"].user_id)

    file_id = document["id"]

    await naive_writer(req, file_id)

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

    return json_response(document, status=201, headers=headers)
Пример #10
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({"_id": index_id}):
        return not_found()

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

    db_query = {
        "index.id": index_id
    }

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

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

    return json_response(data)
Пример #11
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({"_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.db.samples.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.db.samples.RIGHTS_PROJECTION)

    return json_response(document)
Пример #12
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.db.processes.register(
        db, "update_software", 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.db.software.install(req.app, latest_release,
                                         process["id"]))

    return json_response(update)
Пример #13
0
async def get(req):
    """
    Get the complete representation of a specific reference.

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

    ref_id = req.match_info["ref_id"]

    document = await db.references.find_one(ref_id)

    if not document:
        return not_found()

    try:
        internal_control_id = document["internal_control"]["id"]
    except (KeyError, TypeError):
        internal_control_id = None

    computed = await asyncio.shield(
        virtool.db.references.get_computed(db, ref_id, internal_control_id))

    users = await asyncio.shield(
        virtool.db.users.attach_identicons(db, document["users"]))

    document.update({**computed, "users": users})

    return json_response(virtool.utils.base_processor(document))
Пример #14
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.db.references.check_right(req, ref_id, "modify"):
        return insufficient_rights()

    try:
        subdocument = await virtool.db.references.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.db.users.attach_identicons(db, subdocument)

    return json_response(subdocument, headers=headers, status=201)
Пример #15
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.db.references.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.db.processes.register(db,
                                                  "delete_reference",
                                                  context=context)

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

    p = virtool.db.references.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)
Пример #16
0
async def get_isolate(req):
    """
    Get a complete specific isolate sub-document, including its sequences.

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

    if not document:
        return not_found()

    isolate = dict(virtool.otus.find_isolate(document["isolates"], isolate_id),
                   sequences=[])

    cursor = db.sequences.find({
        "otu_id": otu_id,
        "isolate_id": isolate_id
    }, {
        "otu_id": False,
        "isolate_id": False
    })

    async for sequence in cursor:
        sequence["id"] = sequence.pop("_id")
        isolate["sequences"].append(sequence)

    return json_response(isolate)
Пример #17
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.db.groups.update_member_users(db, group_id)

    return json_response(virtool.utils.base_processor(document))
Пример #18
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.get_sample_rights(sample, req["client"])

    if not read:
        return insufficient_rights()

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

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

    return json_response(virtool.utils.base_processor(document))
Пример #19
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.utils.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"].get("data_path"), "logs", "jobs", job_id + ".log")
        await virtool.utils.rm(path)
    except OSError:
        pass

    return no_content()
Пример #20
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))
Пример #21
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()
Пример #22
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.db.indexes.find(db, req.query, ref_id=ref_id)

    return json_response(data)
Пример #23
0
async def list_history(req):
    db = req.app["db"]

    otu_id = req.match_info["otu_id"]

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

    cursor = db.history.find({"otu.id": otu_id})

    return json_response([d async for d in cursor])
Пример #24
0
async def list_groups(req):
    db = req.app["db"]
    ref_id = req.match_info["ref_id"]

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

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

    return json_response(groups)
Пример #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.db.users.PROJECTION)

    if not document:
        return not_found()

    return json_response(virtool.utils.base_processor(document))
Пример #26
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))
Пример #27
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))
Пример #28
0
async def get_api_key(req):
    db = req.app["db"]
    user_id = req["client"].user_id
    key_id = req.match_info.get("key_id")

    document = await db.keys.find_one({
        "id": key_id,
        "user.id": user_id
    }, API_KEY_PROJECTION)

    if document is None:
        return not_found()

    return json_response(document, status=200)
Пример #29
0
async def remove(req):
    file_id = req.match_info["file_id"]

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

    delete_result = await req.app["db"].files.delete_one({"_id": file_id})

    virtool.utils.rm(file_path)

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

    return json_response({"file_id": file_id, "removed": True})
Пример #30
0
async def get_sequence(req):
    """
    Get a single sequence document by its ``accession`.

    """
    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.otus.count({"_id": otu_id, "isolates.id": isolate_id}):
        return not_found()

    query = {"_id": sequence_id, "otu_id": otu_id, "isolate_id": isolate_id}

    document = await db.sequences.find_one(query,
                                           virtool.db.otus.SEQUENCE_PROJECTION)

    if not document:
        return not_found()

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