コード例 #1
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def call_xlsx(cid):
    "Produce an XLSX file of all reviews for a call."
    call = anubis.call.get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    if not anubis.call.allow_view(call):
        return utils.error("You may not view the call.", flask.url_for("home"))
    if not anubis.call.allow_view_reviews(call):
        return utils.error(
            "You may not view the reviews of the call.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    proposals = anubis.proposals.get_call_proposals(call, submitted=True)
    reviews = utils.get_docs_view("reviews", "call", call["identifier"])
    # For ordinary reviewer, list only finalized reviews.
    if not (flask.g.am_admin or anubis.call.am_chair(call)):
        reviews = [
            r for r in reviews
            if r["reviewer"] != flask.g.current_user["username"]
            and r.get("finalized")
        ]
    reviews_lookup = {f"{r['proposal']} {r['reviewer']}": r for r in reviews}
    content = get_reviews_xlsx(call, proposals, reviews_lookup)
    response = flask.make_response(content)
    response.headers.set("Content-Type", constants.XLSX_MIMETYPE)
    response.headers.set("Content-Disposition",
                         "attachment",
                         filename=f"{cid}_reviews.xlsx")
    return response
コード例 #2
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def call_reviewer(cid, username):
    "List all reviews in the call by the reviewer (user)."
    call = anubis.call.get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    user = anubis.user.get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if user["username"] not in call["reviewers"]:
        return utils.error("The user is not a reviewer in the call.",
                           flask.url_for("home"))
    if not (user["username"] == flask.g.current_user["username"]
            or anubis.call.allow_view_reviews(call)):
        return utils.error(
            "You may not view the user's reviews.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    proposals = anubis.proposals.get_call_proposals(call, submitted=True)
    for proposal in proposals:
        proposal["allow_create_review"] = anubis.review.allow_create(proposal)
    reviews = utils.get_docs_view("reviews", "call_reviewer",
                                  [call["identifier"], user["username"]])
    reviews_lookup = {r["proposal"]: r for r in reviews}
    return flask.render_template(
        "reviews/call_reviewer.html",
        call=call,
        proposals=proposals,
        user=user,
        reviews_lookup=reviews_lookup,
    )
コード例 #3
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def proposal_xlsx(pid):
    "Produce an XLSX file of all reviewers and reviews for a proposal."
    proposal = anubis.proposal.get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))
    if not anubis.proposal.allow_view(proposal):
        return utils.error(
            "You may not view the proposal.",
            flask.url_for("call.display", cid=call["identifier"]),
        )
    call = anubis.call.get_call(proposal["call"])
    if not anubis.call.allow_view_reviews(call):
        return utils.error(
            "You may not view the reviews of the call.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    reviews = utils.get_docs_view("reviews", "proposal",
                                  proposal["identifier"])
    if not (flask.g.am_admin or anubis.call.am_chair(call)):
        reviews = [
            r for r in reviews
            if r["reviewer"] != flask.g.current_user["username"]
            and r.get("finalized")
        ]
    reviews_lookup = {f"{pid} {r['reviewer']}": r for r in reviews}
    content = get_reviews_xlsx(call, [proposal], reviews_lookup)
    response = flask.make_response(content)
    response.headers.set("Content-Type", constants.XLSX_MIMETYPE)
    response.headers.set("Content-Disposition",
                         "attachment",
                         filename=f"{pid}_reviews.xlsx")
    return response
コード例 #4
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def reviewer(username):
    """List all reviews by the given reviewer (user).
    If the user is reviewer in only one call, redirect to that page.
    """
    user = anubis.user.get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if not anubis.user.allow_view(user):
        return utils.error(
            "You may not view the user's reviews.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    reviewer_calls = [
        anubis.call.get_call(r.value) for r in flask.g.db.view(
            "calls", "reviewer", key=user["username"], reduce=False)
    ]
    # Reviews in only one call; redirect to its reviews page for the reviewer.
    if len(reviewer_calls) == 1:
        return flask.redirect(
            flask.url_for(
                "reviews.call_reviewer",
                cid=reviewer_calls[0]["identifier"],
                username=username,
            ))

    reviews = utils.get_docs_view("reviews", "reviewer", user["username"])
    return flask.render_template(
        "reviews/reviewer.html",
        user=user,
        reviewer_calls=reviewer_calls,
        reviews=reviews,
    )
コード例 #5
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def call_archived(cid):
    "List all archived reviews in the call."
    call = anubis.call.get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    if not anubis.call.allow_view_reviews(call):
        return utils.error(
            "You may not view the call's reviews.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    proposals = anubis.proposals.get_call_proposals(call, submitted=True)
    reviews = [
        r.doc for r in flask.g.db.view(
            "reviews",
            "call_reviewer_archived",
            startkey=[call["identifier"], ""],
            endkey=[call["identifier"], "ZZZZZZ"],
            include_docs=True,
        )
    ]
    reviews_lookup = {r["proposal"]: r for r in reviews}
    return flask.render_template(
        "reviews/call_archived.html",
        call=call,
        proposals=proposals,
        reviews_lookup=reviews_lookup,
    )
コード例 #6
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def proposal(pid):
    "List all reviewers and reviews for a proposal."
    proposal = anubis.proposal.get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))

    call = anubis.call.get_call(proposal["call"])
    if not anubis.call.allow_view_reviews(call):
        return utils.error(
            "You may not view the reviews of the call.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    reviews = utils.get_docs_view("reviews", "proposal",
                                  proposal["identifier"])
    # For ordinary reviewer, list only finalized reviews.
    if flask.g.am_admin or flask.g.am_staff or anubis.call.am_chair(call):
        only_finalized = False
    else:
        only_finalized = True
        reviews = [r for r in reviews if r.get("finalized")]
    allow_create_review = anubis.review.allow_create(proposal)
    reviews_lookup = {r["reviewer"]: r for r in reviews}
    return flask.render_template(
        "reviews/proposal.html",
        proposal=proposal,
        call=call,
        allow_create_review=allow_create_review,
        reviewers=call["reviewers"],
        reviews_lookup=reviews_lookup,
        only_finalized=only_finalized,
    )
コード例 #7
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def call(cid):
    "List all reviews for a call."
    call = anubis.call.get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    if not anubis.call.allow_view(call):
        return utils.error("You may not view the call.", flask.url_for("home"))
    if not anubis.call.allow_view_reviews(call):
        return utils.error(
            "You may not view the reviews of the call.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    proposals = anubis.proposals.get_call_proposals(call, submitted=True)
    for proposal in proposals:
        proposal["allow_create_review"] = anubis.review.allow_create(proposal)
    reviews = utils.get_docs_view("reviews", "call", call["identifier"])
    # For ordinary reviewer, list only finalized reviews.
    if flask.g.am_admin or flask.g.am_staff or anubis.call.am_chair(call):
        only_finalized = False
    else:
        only_finalized = True
        reviews = [r for r in reviews if r.get("finalized")]
    reviews_lookup = {f"{r['proposal']} {r['reviewer']}": r for r in reviews}
    return flask.render_template(
        "reviews/call.html",
        call=call,
        proposals=proposals,
        reviews_lookup=reviews_lookup,
        only_finalized=only_finalized,
    )
コード例 #8
0
ファイル: grant.py プロジェクト: pekrau/Anubis
def document(gid, fid):
    "Download the grant document (attachment file) for the given field id."
    try:
        grant = get_grant(gid)
    except KeyError:
        return utils.error("No such grant dossier.")
    if not allow_view(grant):
        return utils.error("You are not allowed to read this grant dossier.")

    try:
        documentname = grant["values"][fid]
        stub = grant["_attachments"][documentname]
    except KeyError:
        return utils.error("No such document in grant dossier.")
    # Colon ':' is a problematic character in filenames; replace by dash '-'.
    gid = gid.replace(":", "-")
    ext = os.path.splitext(documentname)[1]
    # Add the appropriate file extension to the filename.
    filename = f"{gid}-{fid}{ext}"
    outfile = flask.g.db.get_attachment(grant, documentname)
    response = flask.make_response(outfile.read())
    response.headers.set("Content-Type", stub["content_type"])
    response.headers.set("Content-Disposition",
                         "attachment",
                         filename=filename)
    return response
コード例 #9
0
ファイル: call.py プロジェクト: pekrau/Anubis
def create_proposal(cid):
    "Create a new proposal within the call. Redirect to an existing proposal."
    call = get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    if not anubis.proposal.allow_create(call):
        return utils.error("You may not create a proposal.")

    if utils.http_POST():
        proposal = anubis.proposal.get_call_user_proposal(
            cid, flask.g.current_user["username"]
        )
        if proposal:
            return utils.message(
                "Proposal already exists for the call.",
                flask.url_for("proposal.display", pid=proposal["identifier"]),
            )
        else:
            with anubis.proposal.ProposalSaver(
                call=call, user=flask.g.current_user
            ) as saver:
                pass
            return flask.redirect(
                flask.url_for("proposal.edit", pid=saver.doc["identifier"])
            )
コード例 #10
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def call_reviewer_xlsx(cid, username):
    "Produce an XLSX file of all reviews in the call by the reviewer (user)."
    call = anubis.call.get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    user = anubis.user.get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if user["username"] not in call["reviewers"]:
        return utils.error("The user is not a reviewer in the call.",
                           flask.url_for("home"))
    if not (user["username"] == flask.g.current_user["username"]
            or anubis.call.allow_view_reviews(call)):
        return utils.error(
            "You may not view the user's reviews.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    proposals = anubis.proposals.get_call_proposals(call, submitted=True)
    reviews = utils.get_docs_view("reviews", "call_reviewer",
                                  [call["identifier"], user["username"]])
    reviews_lookup = {f"{r['proposal']} {username}": r for r in reviews}
    content = get_reviews_xlsx(call, proposals, reviews_lookup)
    response = flask.make_response(content)
    response.headers.set("Content-Type", constants.XLSX_MIMETYPE)
    response.headers.set("Content-Disposition",
                         "attachment",
                         filename=f"{cid}_{username}_reviews.xlsx")
    return response
コード例 #11
0
def transfer(pid):
    "Transfer ownership of he proposal."
    proposal = get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))
    if not allow_transfer(proposal):
        return utils.error("You are not allowed to transfer ownership of"
                           " this proposal.")

    if utils.http_GET():
        return flask.render_template("proposal/transfer.html",
                                     proposal=proposal)

    elif utils.http_POST():
        try:
            with ProposalSaver(proposal) as saver:
                value = flask.request.form.get("user")
                if value:
                    user = anubis.user.get_user(username=value, email=value)
                    if user:
                        saver.set_user(user)
                    else:
                        raise ValueError("No such user.")
        except ValueError as error:
            return utils.error(error)
        return flask.redirect(
            flask.url_for(".display", pid=proposal["identifier"]))
コード例 #12
0
ファイル: review.py プロジェクト: pekrau/Anubis
def display(iuid):
    "Display the review for the proposal."
    try:
        review = get_review(iuid)
    except KeyError:
        return utils.error("No such review.", flask.url_for("home"))
    call = anubis.call.get_call(review["call"])
    proposal = anubis.proposal.get_proposal(review["proposal"])
    if not allow_view(review):
        return utils.error(
            "You are not allowed to view this review.",
            flask.url_for("proposal.display", pid=review["proposal"]),
        )
    allow_view_reviews = anubis.call.allow_view_reviews(call)
    return flask.render_template(
        "review/display.html",
        review=review,
        call=call,
        proposal=proposal,
        allow_edit=allow_edit(review),
        allow_delete=allow_delete(review),
        allow_finalize=allow_finalize(review),
        allow_unfinalize=allow_unfinalize(review),
        allow_view_reviews=allow_view_reviews,
    )
コード例 #13
0
def display(username):
    "Display the given user."
    user = get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if not allow_view(user):
        return utils.error("Access to user display not allowed.")
    reviewer_calls = [
        anubis.call.get_call(r.value) for r in flask.g.db.view(
            "calls", "reviewer", key=user["username"], reduce=False)
    ]
    user_proposals_count = utils.get_count(
        "proposals", "user", user["username"]) + utils.get_count(
            "proposals", "access", user["username"])
    return flask.render_template(
        "user/display.html",
        user=user,
        reviewer_calls=reviewer_calls,
        allow_create_call=anubis.call.allow_create(user),
        user_calls_count=utils.get_count("calls", "owner", user["username"]),
        user_proposals_count=user_proposals_count,
        user_reviews_count=utils.get_count("reviews", "reviewer",
                                           user["username"]),
        user_grants_count=utils.get_user_grants_count(user["username"]),
        allow_enable_disable=allow_enable_disable(user),
        allow_edit=allow_edit(user),
        allow_delete=allow_delete(user),
        gdpr=utils.get_site_text("gdpr.md"),
    )
コード例 #14
0
ファイル: grant.py プロジェクト: pekrau/Anubis
def display(gid):
    "Display the grant dossier."
    grant = get_grant(gid)
    if grant is None:
        return utils.error("No such grant dossier.")
    if not allow_view(grant):
        return utils.error("You are not allowed to view this grant dossier.")
    receiver_email = anubis.user.get_user(username=grant["user"])["email"]
    access_emails = []
    for username in grant.get("access_view", []):
        user = anubis.user.get_user(username=username)
        if user:
            access_emails.append(user["email"])
    # There may be accounts that have no email!
    access_emails = [e for e in access_emails if e]
    all_emails = [receiver_email] + access_emails
    email_lists = {
        "Grant receiver (= proposal submitter)": receiver_email,
        "Persons with access to this grant": ", ".join(access_emails),
        "All involved persons": ", ".join(all_emails),
    }
    return flask.render_template(
        "grant/display.html",
        grant=grant,
        proposal=anubis.proposal.get_proposal(grant["proposal"]),
        call=anubis.call.get_call(grant["call"]),
        call_grants_count=utils.get_count("grants", "call", gid),
        email_lists=email_lists,
        allow_view=allow_view(grant),
        allow_edit=allow_edit(grant),
        allow_change_access=allow_change_access(grant),
        allow_lock=allow_lock(grant),
        allow_delete=allow_delete(grant),
    )
コード例 #15
0
ファイル: review.py プロジェクト: pekrau/Anubis
def document(iuid, fid):
    "Download the review document (attachment file) for the given field id."
    try:
        review = get_review(iuid)
    except KeyError:
        return utils.error("No such review.", flask.url_for("home"))
    if not allow_view(review):
        return utils.error("You are not allowed to read this review.",
                           flask.url_for("home"))

    try:
        documentname = review["values"][fid]
        stub = review["_attachments"][documentname]
    except KeyError:
        return utils.error("No such document in review.")
    # Colon ':' is a problematic character in filenames.
    # Replace it by dash '-'; used as general glue character here.
    pid = review["proposal"].replace(":", "-")
    ext = os.path.splitext(documentname)[1]
    # Include reviewer id in filename to indicate review document.
    filename = f"{pid}-{review['reviewer']}-{fid}{ext}"
    outfile = flask.g.db.get_attachment(review, documentname)
    response = flask.make_response(outfile.read())
    response.headers.set("Content-Type", stub["content_type"])
    response.headers.set("Content-Disposition",
                         "attachment",
                         filename=filename)
    return response
コード例 #16
0
ファイル: call.py プロジェクト: pekrau/Anubis
def display(cid):
    "Display the call."
    call = get_call(cid)
    if not call:
        return utils.error("No such call.", flask.url_for("home"))
    if not allow_view(call):
        return utils.error("You are not allowed to view the call.")
    kwargs = {}
    if allow_view_details(call):
        reviewers = [anubis.user.get_user(r) for r in call["reviewers"]]
        reviewer_emails = [r["email"] for r in reviewers if r["email"]]
        access_emails = []
        for username in [call["owner"]] + call.get("access_view", []):
            user = anubis.user.get_user(username=username)
            if user:
                access_emails.append(user["email"])
        # There may be accounts that have no email!
        access_emails = [e for e in access_emails if e]
        all_emails = reviewer_emails + access_emails
        email_lists = {
            "Persons with access to this call": ", ".join(access_emails),
            "Emails for reviewers": ", ".join(reviewer_emails),
            "All involved persons": ", ".join(all_emails),
        }
        kwargs["email_lists"] = email_lists
    if flask.g.current_user:
        kwargs["my_proposal"] = anubis.proposal.get_call_user_proposal(
            cid, flask.g.current_user["username"]
        )
        kwargs["my_reviews_count"] = utils.get_call_reviewer_reviews_count(
            cid, flask.g.current_user["username"]
        )
        kwargs["my_archived_reviews_count"] = utils.get_call_reviewer_reviews_count(
            cid, flask.g.current_user["username"], archived=True
        )
    kwargs["call_proposals_count"] = utils.get_call_proposals_count(cid)
    # Number of archived reviews for the call.
    result = flask.g.db.view(
        "reviews",
        "call_reviewer_archived",
        startkey=[call["identifier"], ""],
        endkey=[call["identifier"], "ZZZZZZ"],
        reduce=True,
    )
    kwargs["archived_reviews_count"] = result and result[0].value or 0
    return flask.render_template(
        "call/display.html",
        call=call,
        am_owner=am_owner(call),
        am_reviewer=am_reviewer(call),
        allow_edit=allow_edit(call),
        allow_delete=allow_delete(call),
        allow_change_access=allow_change_access(call),
        allow_create_proposal=anubis.proposal.allow_create(call),
        allow_view_details=allow_view_details(call),
        allow_view_proposals=allow_view_proposals(call),
        allow_view_reviews=allow_view_reviews(call),
        allow_view_grants=allow_view_grants(call),
        **kwargs,
    )
コード例 #17
0
ファイル: call.py プロジェクト: pekrau/Anubis
def grant(cid):
    "Display grant field definitions for delete, and add field."
    call = get_call(cid)
    if not call:
        return utils.error("No such call.", flask.url_for("home"))
    if not allow_edit(call):
        return utils.error("You are not allowed to edit the call.")

    if utils.http_GET():
        repeat_fields = [
            f for f in call.get("grant", []) if f["type"] == constants.REPEAT
        ]
        return flask.render_template(
            "call/grant.html",
            call=call,
            repeat_fields=repeat_fields,
            reviews_count=utils.get_count("reviews", "call", call["identifier"]),
        )

    elif utils.http_POST():
        try:
            with CallSaver(call) as saver:
                saver.add_grant_field(flask.request.form)
        except ValueError as error:
            utils.flash_error(error)
        return flask.redirect(flask.url_for(".grant", cid=call["identifier"]))
コード例 #18
0
ファイル: call.py プロジェクト: pekrau/Anubis
def document(cid, documentname):
    "Download the given document (attachment file), or delete it."
    call = get_call(cid)
    if not call:
        return utils.error("No such call.", flask.url_for("home"))

    if utils.http_GET():
        if not allow_view(call):
            return utils.error(f"You may not view the call {call['title']}.")
        try:
            stub = call["_attachments"][documentname]
        except KeyError:
            return utils.error("No such document in call.")
        outfile = flask.g.db.get_attachment(call, documentname)
        response = flask.make_response(outfile.read())
        response.headers.set("Content-Type", stub["content_type"])
        response.headers.set("Content-Disposition", "attachment", filename=documentname)
        return response

    elif utils.http_DELETE():
        if not allow_edit(call):
            return utils.error("You are not allowed to edit the call.")
        with CallSaver(call) as saver:
            saver.delete_document(documentname)
        return flask.redirect(flask.url_for(".documents", cid=call["identifier"]))
コード例 #19
0
def call_zip(cid):
    """Return a zip file containing the XLSX file of all grants for a call
    and all documents in all grant dossiers.
    """
    call = anubis.call.get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    if not anubis.call.allow_view(call):
        return utils.error("You may not view the call.", flask.url_for("home"))
    if not anubis.call.allow_view_grants(call):
        return utils.error(
            "You may not view the grants of the call.",
            flask.url_for("call.display", cid=call["identifier"]),
        )
    # Colon ':' is a problematic character in filenames; replace by dash '_'
    cid = cid.replace(":", "-")
    grants = utils.get_docs_view("grants", "call", call["identifier"])
    output = io.BytesIO()
    with zipfile.ZipFile(output, "w") as outfile:
        outfile.writestr(f"{cid}_grants.xlsx", get_call_grants_xlsx(call, grants))
        for grant in grants:
            for document in anubis.grant.get_grant_documents(grant):
                outfile.writestr(document["filename"], document["content"])
    response = flask.make_response(output.getvalue())
    response.headers.set("Content-Type", constants.ZIP_MIMETYPE)
    response.headers.set(
        "Content-Disposition", "attachment", filename=f"{cid}_grants.zip"
    )
    return response
コード例 #20
0
ファイル: reviews.py プロジェクト: pekrau/Anubis
def call_reviewer_zip(cid, username):
    """Return a zip file containing the XLSX file of all reviews
    in the call by the reviewer (user), and all documents for the proposals
    to be reviewed.
    """
    call = anubis.call.get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))
    user = anubis.user.get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if user["username"] not in call["reviewers"]:
        return utils.error("The user is not a reviewer in the call.",
                           flask.url_for("home"))
    if not (user["username"] == flask.g.current_user["username"]
            or anubis.call.allow_view_reviews(call)):
        return utils.error(
            "You may not view the user's reviews.",
            flask.url_for("call.display", cid=call["identifier"]),
        )

    proposals = anubis.proposals.get_call_proposals(call, submitted=True)
    reviews = utils.get_docs_view("reviews", "call_reviewer",
                                  [call["identifier"], user["username"]])
    reviews_lookup = {f"{r['proposal']} {username}": r for r in reviews}
    output = io.BytesIO()
    with zipfile.ZipFile(output, "w") as zip:
        zip.writestr(
            f"{cid}_{username}_reviews.xlsx",
            get_reviews_xlsx(call, proposals, reviews_lookup),
        )
        # Filter away proposals not to be reviewed by the user.
        proposals = [
            p for p in proposals
            if f"{p['identifier']} {username}" in reviews_lookup
        ]
        zip.writestr(
            f"{call['identifier']}_selected_proposals.xlsx",
            anubis.proposals.get_call_xlsx(call, proposals=proposals),
        )
        for proposal in proposals:
            for field in call["proposal"]:
                if field["type"] == constants.DOCUMENT:
                    try:
                        doc = anubis.proposal.get_document(
                            proposal, field["identifier"])
                    except KeyError:
                        pass
                    else:
                        zip.writestr(doc["filename"], doc["content"])
    response = flask.make_response(output.getvalue())
    response.headers.set("Content-Type", constants.ZIP_MIMETYPE)
    response.headers.set(
        "Content-Disposition",
        "attachment",
        filename=f"{call['identifier']}_reviewer_{username}.zip",
    )
    return response
コード例 #21
0
def user(username):
    "List all grants for a user, including the grants the user has access to."
    user = anubis.user.get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if not anubis.user.allow_view(user):
        return utils.error("You may not view the user's grants.", flask.url_for("home"))
    grants = utils.get_docs_view("grants", "user", user["username"])
    grants.extend(utils.get_docs_view("grants", "access", user["username"]))
    return flask.render_template("grants/user.html", user=user, grants=grants)
コード例 #22
0
def display(pid):
    "Display the proposal."
    proposal = get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))
    if not allow_view(proposal):
        return utils.error("You are not allowed to view this proposal.")
    call = anubis.call.get_call(proposal["call"])
    am_submitter = (flask.g.current_user
                    and flask.g.current_user["username"] == proposal["user"])
    submitter_email = anubis.user.get_user(username=proposal["user"])["email"]
    access_emails = []
    for username in proposal.get("access_view", []):
        user = anubis.user.get_user(username=username)
        if user:
            access_emails.append(user["email"])
    # There may be accounts that have no email!
    access_emails = [e for e in access_emails if e]
    all_emails = [submitter_email] + access_emails
    email_lists = {
        "Proposal submitter": submitter_email,
        "Persons with access to this proposal": ", ".join(access_emails),
        "All involved persons": ", ".join(all_emails),
    }
    decision = anubis.decision.get_decision(proposal.get("decision"))
    # Only show decision in-line in proposal for non-admin or non-staff.
    allow_view_decision = (decision and decision.get("finalized")
                           and not (flask.g.am_admin or flask.g.am_staff) and
                           call["access"].get("allow_submitter_view_decision"))
    grant = anubis.grant.get_grant_proposal(proposal["identifier"])
    return flask.render_template(
        "proposal/display.html",
        proposal=proposal,
        call=call,
        decision=decision,
        grant=grant,
        email_lists=email_lists,
        allow_edit=allow_edit(proposal),
        allow_delete=allow_delete(proposal),
        allow_submit=allow_submit(proposal),
        allow_transfer=allow_transfer(proposal),
        am_submitter=am_submitter,
        am_reviewer=anubis.call.am_reviewer(call),
        my_review=anubis.review.get_reviewer_review(proposal,
                                                    flask.g.current_user),
        allow_view_reviews=anubis.call.allow_view_reviews(call),
        allow_create_decision=anubis.decision.allow_create(proposal),
        allow_link_decision=anubis.decision.allow_link(decision),
        allow_view_decision=allow_view_decision,
        allow_create_grant=anubis.grant.allow_create(proposal),
        allow_link_grant=anubis.grant.allow_link(grant),
    )
コード例 #23
0
def logs(username):
    "Display the log records for the given user account."
    user = get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if not allow_view(user):
        return utils.error("Access to user logs not allowed.")
    return flask.render_template(
        "logs.html",
        title=f"User {user['username']}",
        back_url=flask.url_for(".display", username=user["username"]),
        logs=utils.get_logs(user["_id"]),
    )
コード例 #24
0
def edit(username):
    "Edit the user. Or delete the user."
    user = get_user(username=username)
    if user is None:
        return utils.error("No such user.", flask.url_for("home"))
    if not allow_edit(user):
        return utils.error("Access to user edit not allowed.")

    if utils.http_GET():
        return flask.render_template("user/edit.html",
                                     user=user,
                                     allow_change_role=allow_change_role(user))

    elif utils.http_POST():
        try:
            with UserSaver(user) as saver:
                if flask.g.am_admin:
                    email = flask.request.form.get("email")
                    saver.set_email(email, require=bool(email))
                if allow_change_role(user):
                    saver.set_role(flask.request.form.get("role"))
                    saver.set_call_creator(
                        utils.to_bool(flask.request.form.get("call_creator")))
                saver.set_givenname(flask.request.form.get("givenname"))
                saver.set_familyname(flask.request.form.get("familyname"))
                saver.set_gender(flask.request.form.get("gender"))
                saver.set_birthdate(flask.request.form.get("birthdate"))
                saver.set_degree(flask.request.form.get("degree"))
                saver.set_affiliation(
                    flask.request.form.get("affiliation")
                    or flask.request.form.get("affiliation_other"))
                saver.set_postaladdress(
                    flask.request.form.get("postaladdress"))
                saver.set_phone(flask.request.form.get("phone"))
        except ValueError as error:
            utils.flash_error(error)
        return flask.redirect(
            flask.url_for(".display", username=user["username"]))

    elif utils.http_DELETE():
        if not allow_delete(user):
            return utils.error(
                "Cannot delete the user account; admin or not empty.",
                flask.url_for(".display", username=username),
            )
        flask.g.db.delete(user)
        utils.flash_message(f"Deleted user {username}.")
        if flask.g.am_admin:
            return flask.redirect(flask.url_for(".all"))
        else:
            return flask.redirect(flask.url_for("home"))
コード例 #25
0
def display_xlsx(pid):
    "Return an XLSX file containing the proposal information."
    proposal = get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))
    if not allow_view(proposal):
        return utils.error("You are not allowed to view this proposal.")
    content = get_proposal_xlsx(proposal).getvalue()
    response = flask.make_response(content)
    response.headers.set("Content-Type", constants.XLSX_MIMETYPE)
    response.headers.set("Content-Disposition",
                         "attachment",
                         filename=f"{pid.replace(':','-')}.xlsx")
    return response
コード例 #26
0
ファイル: grant.py プロジェクト: pekrau/Anubis
def logs(gid):
    "Display the log records of the given grant dossier."
    grant = get_grant(gid)
    if grant is None:
        return utils.error("No such grant dossier.")
    if not allow_view(grant):
        return utils.error("You are not allowed to read this grant dossier.")

    return flask.render_template(
        "logs.html",
        title=f"Grant {grant['identifier']}",
        back_url=flask.url_for(".display", gid=grant["identifier"]),
        logs=utils.get_logs(grant["_id"]),
    )
コード例 #27
0
ファイル: call.py プロジェクト: pekrau/Anubis
def logs(cid):
    "Display the log records of the call."
    call = get_call(cid)
    if call is None:
        return utils.error("No such call.", flask.url_for("home"))

    if not (flask.g.am_admin or am_owner(call)):
        return utils.error("You are not admin or owner of the call.")

    return flask.render_template(
        "logs.html",
        title=f"Call {call['identifier']}",
        back_url=flask.url_for(".display", cid=call["identifier"]),
        logs=utils.get_logs(call["_id"]),
    )
コード例 #28
0
def logs(pid):
    "Display the log records of the given proposal."
    proposal = get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))
    if not allow_view(proposal):
        return utils.error("You are not allowed to read this proposal.",
                           flask.url_for("home"))

    return flask.render_template(
        "logs.html",
        title=f"Proposal {proposal['identifier']}",
        back_url=flask.url_for(".display", pid=proposal["identifier"]),
        logs=utils.get_logs(proposal["_id"]),
    )
コード例 #29
0
def call_xlsx(cid):
    "Produce an XLSX file of all proposals in a call."
    call = anubis.call.get_call(cid)
    if not call:
        return utils.error("No such call.", flask.url_for("home"))
    if not anubis.call.allow_view(call):
        return utils.error("You may not view the call.", flask.url_for("home"))
    submitted = utils.to_bool(flask.request.args.get("submitted", ""))
    response = flask.make_response(get_call_xlsx(call, submitted=submitted))
    response.headers.set("Content-Type", constants.XLSX_MIMETYPE)
    response.headers.set(
        "Content-Disposition",
        "attachment",
        filename=f"{call['identifier']}_proposals.xlsx",
    )
    return response
コード例 #30
0
ファイル: review.py プロジェクト: pekrau/Anubis
def create(pid, username):
    "Create a new review for the proposal for the given reviewer."
    proposal = anubis.proposal.get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))
    call = anubis.call.get_call(proposal["call"])

    try:
        if not allow_create(proposal):
            raise ValueError("You may not create a review for the proposal.")
        user = anubis.user.get_user(username=username)
        if user is None:
            raise ValueError("No such user.")
        if user["username"] not in call["reviewers"]:
            raise ValueError("User is not a reviewer in the call.")
        review = get_reviewer_review(proposal, user)
        if review is not None:
            utils.flash_message("The review already exists.")
            return flask.redirect(flask.url_for(".display",
                                                iuid=review["_id"]))
        if proposal["user"] == user["username"]:
            raise ValueError(
                "Reviewer not allowed to review their own proposal.")
        with ReviewSaver(proposal=proposal, user=user) as saver:
            pass
    except ValueError as error:
        utils.flash_error(error)
    return flask.redirect(
        flask.url_for("reviews.call_reviewer",
                      cid=proposal["call"],
                      username=username))