Beispiel #1
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()
Beispiel #2
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)
Beispiel #3
0
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
Beispiel #4
0
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))
Beispiel #5
0
async def remove(req):
    db = req.app["db"]
    settings = req.app["settings"]

    subtraction_id = req.match_info["subtraction_id"]

    if await db.samples.count({"subtraction.id": subtraction_id}):
        return conflict("Has linked samples")

    delete_result = await db.subtraction.delete_one({"_id": subtraction_id})

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

    index_path = virtool.subtractions.utils.calculate_index_path(
        settings, subtraction_id)

    await req.app["run_in_thread"](shutil.rmtree, index_path, True)

    return no_content()
Beispiel #6
0
async def cancel(req):
    """
    Cancel a job.

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

    job_id = req.match_info["job_id"]

    document = await db.jobs.find_one(job_id, ["status"])

    if not document:
        return not_found()

    if not virtool.jobs.is_running_or_waiting(document):
        return conflict("Not cancellable")

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

    document = await db.jobs.find_one(job_id)

    return json_response(virtool.utils.base_processor(document))
Beispiel #7
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_algorithm_tags(db, sample_id)

    return no_content()
Beispiel #8
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(db, change_id)
    except virtool.errors.DatabaseError:
        return conflict("Change is already built")

    return no_content()
Beispiel #9
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.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)
Beispiel #10
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({"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)