Exemplo n.º 1
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)
Exemplo n.º 2
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)
Exemplo n.º 3
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))
Exemplo n.º 4
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)
Exemplo n.º 5
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()
Exemplo n.º 6
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)
Exemplo n.º 7
0
async def create_sequence(req):
    """
    Create a new sequence record for the given isolate.

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

    otu_id = req.match_info["otu_id"]
    isolate_id = req.match_info["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}, ["reference", "schema"])

    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()

    message = await virtool.otus.sequences.check_segment_or_target(
        db,
        otu_id,
        isolate_id,
        None,
        ref_id,
        data
    )

    if message:
        return bad_request(message)

    sequence_document = await asyncio.shield(virtool.otus.sequences.create(
        req.app,
        ref_id,
        otu_id,
        isolate_id,
        data,
        req["client"].user_id
    ))

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

    return json_response(sequence_document, status=201, headers=headers)
Exemplo n.º 8
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()

    try:
        iso = virtool.api.json.isoformat(document["updated_at"])
    except KeyError:
        iso = virtool.api.json.isoformat(document["created_at"])

    if_modified_since = req.headers.get("If-Modified-Since")

    if if_modified_since and if_modified_since == iso:
        return virtool.api.response.not_modified()

    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)

    headers = {
        "Cache-Control": "no-cache",
        "Last-Modified": virtool.api.json.isoformat(document["created_at"])
    }

    return json_response(virtool.utils.base_processor(document), headers=headers)
Exemplo n.º 9
0
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.
    document = await db.otus.find_one(otu_id, ["abbreviation", "name", "reference", "schema"])

    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()

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

    # 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)

    document = await asyncio.shield(virtool.otus.db.edit(
        req.app,
        otu_id,
        name,
        abbreviation,
        schema,
        req["client"].user_id
    ))

    return json_response(document)
Exemplo n.º 10
0
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_workflow_tags(db, sample_id)

    return no_content()
Exemplo n.º 11
0
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 asyncio.shield(
        virtool.references.db.update(req, created_at, process["id"], ref_id,
                                     release, user_id))

    return json_response(update_subdocument, status=201)
Exemplo n.º 12
0
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)

    document = await asyncio.shield(virtool.otus.db.create(
        req.app,
        ref_id,
        name,
        abbreviation,
        req["client"].user_id
    ))

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

    return json_response(document, status=201, headers=headers)
Exemplo n.º 13
0
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, ["reference"])

    if not document:
        return not_found()

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

        # 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")

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

    headers = {
        "Location": f"/api/otus/{otu_id}/isolates/{isolate['id']}"
    }

    return json_response(
        isolate,
        status=201,
        headers=headers
    )
Exemplo n.º 14
0
async def edit_sequence(req):
    db = req.app["db"]
    data = req["data"]

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

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

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

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

    message = await virtool.otus.sequences.check_segment_or_target(
        db,
        otu_id,
        isolate_id,
        sequence_id,
        document["reference"]["id"],
        data
    )

    if message:
        return bad_request(message)

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

    return json_response(sequence_document)
Exemplo n.º 15
0
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()

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

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

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

    return json_response(isolate, status=200)
Exemplo n.º 16
0
async def edit(req):
    db = req.app["db"]
    data = req["data"]

    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, "modify"):
        return insufficient_rights()

    targets = data.get("targets")

    if targets:
        names = [t["name"] for t in targets]

        if len(names) != len(set(names)):
            return bad_request(
                "The targets field may not contain duplicate names")

    document = await virtool.references.db.edit(db, ref_id, data)

    return json_response(document)
Exemplo n.º 17
0
async def revert(req):
    """
    Remove the change document with the given ``change_id`` and any subsequent changes.

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

    change_id = req.match_info["change_id"]

    document = await db.history.find_one(change_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()

    try:
        await virtool.history.db.revert(req.app, change_id)
    except virtool.errors.DatabaseError:
        return conflict("Change is already built")

    return no_content()
Exemplo n.º 18
0
async def remove(req):
    """
    Remove an OTU document and its associated sequence documents.

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

    otu_id = req.match_info["otu_id"]

    document = await db.otus.find_one(otu_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.db.remove(
        req.app,
        otu_id,
        req["client"].user_id
    ))

    return web.Response(status=204)
Exemplo n.º 19
0
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_documents({"reference.id": ref_id, "ready": False}):
        return conflict("Index build already in progress")

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

    if not await db.history.count_documents({"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)