Esempio n. 1
0
async def update(req):
    """
    Update application settings based on request data.

    """
    raw_data = await req.json()
    data = {key: req["data"][key] for key in raw_data}

    proc = data.get("proc", None)
    mem = data.get("mem", None)

    settings = req.app["settings"]

    error_message = virtool.settings.check_resource_limits(
        proc, mem, settings.data)

    if error_message:
        return conflict(error_message)

    proc = proc or settings["proc"]
    mem = mem or settings["mem"]

    error_message = virtool.settings.check_task_specific_limits(
        proc, mem, data)

    if error_message:
        return conflict(error_message)

    app_settings = req.app["settings"]

    app_settings.update(data)

    await app_settings.write()

    return json_response(app_settings.data)
Esempio 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", "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)
Esempio n. 3
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.db.processes.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.db.hmm.install(req.app, process["id"], release, user_id))

    return json_response(update)
Esempio n. 4
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()
Esempio n. 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.calculate_index_path(settings, subtraction_id)

    await req.loop.run_in_executor(None, shutil.rmtree, index_path, True)

    return no_content()
Esempio n. 6
0
async def edit(req):
    db = req.app["db"]
    data = await req.json()
    settings = req.app["settings"]

    if "password" in data and len(
            data["password"]) < settings["minimum_password_length"]:
        return bad_request("Password does not meet length requirement")

    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.db.users.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.db.users.PROJECTION)

    return json_response(virtool.utils.base_processor(projected))
Esempio n. 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.db.samples.PROJECTION)

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

    read, write = virtool.samples.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.db.samples.recalculate_algorithm_tags(db, sample_id)

    return no_content()
Esempio n. 8
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.utils.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))
Esempio n. 9
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.db.references.check_right(req, document["reference"]["id"], "modify_otu"):
        return insufficient_rights()

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

    return no_content()
Esempio n. 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.db.references.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.db.indexes.get_next_version(db, ref_id)

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

    manifest = await virtool.db.references.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.db.jobs.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)