Ejemplo n.º 1
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)
Ejemplo n.º 2
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.downloads.db.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}"})
Ejemplo n.º 3
0
async def download_sample_reads(req):
    db = req.app["db"]

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

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

    if not files:
        return not_found()

    suffix = req.match_info["suffix"]
    sample_path = virtool.samples.utils.join_sample_path(
        req.app["settings"], sample_id)

    if extension == "fastq" or extension == "fq":
        path = virtool.samples.utils.join_legacy_read_path(sample_path, suffix)
    else:
        path = virtool.samples.utils.join_read_path(sample_path, suffix)

    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)
Ejemplo n.º 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)
Ejemplo n.º 5
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.references.db.check_right(
            req, document["reference"]["id"], "modify_otu"):
        return insufficient_rights()

    old = await virtool.otus.db.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.otus.db.join(db, otu_id, document)

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

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

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

    return json_response(virtool.utils.base_processor(updated_sequence))
Ejemplo n.º 6
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.otus.db.join(db, {
        "_id": otu_id,
        "isolates.id": isolate_id
    })

    if old is None:
        return not_found()

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

    isolate = virtool.otus.utils.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.otus.db.join(db, otu_id)

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

    isolate_name = virtool.otus.utils.format_isolate_name(isolate)

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

    return no_content()
Ejemplo n.º 7
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))
Ejemplo n.º 8
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)
Ejemplo n.º 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.files.db.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)
Ejemplo n.º 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({"_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", 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.samples.db.RIGHTS_PROJECTION)

    return json_response(document)
Ejemplo n.º 11
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.references.db.get_computed(db, ref_id, internal_control_id))

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

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

    return json_response(virtool.utils.base_processor(document))
Ejemplo n.º 12
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()
Ejemplo n.º 13
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.history.db.LIST_PROJECTION,
                          reverse=True)

    return json_response(data)
Ejemplo n.º 14
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()

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

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

    return json_response(virtool.utils.base_processor(document))
Ejemplo n.º 15
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)
Ejemplo n.º 16
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)
Ejemplo n.º 17
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()
Ejemplo n.º 18
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])
Ejemplo n.º 19
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))
Ejemplo n.º 20
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)
Ejemplo n.º 21
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)
Ejemplo n.º 22
0
async def get_api_key(req):
    db = req.app["db"]
    user_id = req["client"].user_id
    key_id = req.match_info["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)
Ejemplo n.º 23
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))
Ejemplo n.º 24
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})
Ejemplo n.º 25
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))
Ejemplo n.º 26
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))
Ejemplo n.º 27
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.otus.db.SEQUENCE_PROJECTION)

    if not document:
        return not_found()

    return json_response(virtool.utils.base_processor(document))
Ejemplo n.º 28
0
async def get(req):
    """
    Return the complete representation for the cache with the given `cache_id`.

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

    cache = await virtool.caches.db.get(db, cache_id)

    if cache is None:
        return not_found()

    return json_response(cache)
Ejemplo n.º 29
0
async def remove_api_key(req):
    db = req.app["db"]
    user_id = req["client"].user_id
    key_id = req.match_info["key_id"]

    delete_result = await db.keys.delete_one({
        "id": key_id,
        "user.id": user_id
    })

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

    return no_content()
Ejemplo n.º 30
0
async def get(req):
    """
    Get a complete otu document. Joins the otu document with its associated sequence documents.

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

    otu_id = req.match_info["otu_id"]

    complete = await virtool.otus.db.join_and_format(db, otu_id)

    if not complete:
        return not_found()

    return json_response(complete)