Example #1
0
def allow_create(proposal):
    "Admin and chair may create a decision for a submitted proposal."
    if not flask.g.current_user:
        return False
    if not proposal.get("submitted"):
        return False
    if proposal.get("decision"):
        return False
    if flask.g.am_admin:
        return True
    call = anubis.call.get_call(proposal["call"])
    if anubis.call.am_owner(call):
        return True
    if anubis.call.am_chair(call):
        return True
    return False
Example #2
0
def allow_create(proposal):
    "The admin, call owner and chair may create a review for a submitted proposal."
    if not proposal.get("submitted"):
        return False
    if not flask.g.current_user:
        return False
    if flask.g.am_admin:
        return True
    call = anubis.call.get_call(proposal["call"])
    if anubis.call.am_chair(call) and call["access"].get(
            "allow_chair_create_reviews"):
        return True
    if anubis.call.am_owner(call):
        return True
    return False
Example #3
0
def allow_create(proposal):
    "The admin and staff may create a grant dossier."
    if not flask.g.current_user:
        return False
    if not proposal.get("decision"):
        return False
    decision = anubis.decision.get_decision(proposal["decision"])
    if not decision.get("finalized"):
        return False
    if not decision.get("verdict"):
        return False
    if flask.g.am_admin:
        return True
    if flask.g.am_staff:
        return True
    return False
Example #4
0
def call(cid):
    "List 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"))
    proposals = get_call_proposals(call)
    all_emails = []
    submitted_emails = []
    for proposal in proposals:
        user = anubis.user.get_user(username=proposal["user"])
        if not user:
            continue
        all_emails.append(user["email"])
        if proposal.get("submitted"):
            submitted_emails.append(user["email"])
    # There may be accounts that have no email!
    all_emails = sorted(set([e for e in all_emails if e]))
    submitted_emails = sorted(set([e for e in submitted_emails if e]))
    email_lists = {
        "Emails to for submitted proposals": ", ".join(submitted_emails),
        "Emails for all proposals": ", ".join(all_emails),
    }
    rank_fields, rank_errors = get_review_rank_fields_errors(call, proposals)
    for error in rank_errors:
        utils.flash_warning(error)
    return flask.render_template(
        "proposals/call.html",
        call=call,
        proposals=proposals,
        email_lists=email_lists,
        review_score_fields=get_review_score_fields(call, proposals),
        review_rank_fields=rank_fields,
        review_rank_errors=rank_errors,
        am_reviewer=anubis.call.am_reviewer(call),
        allow_view_details=anubis.call.allow_view_details(call),
        allow_view_reviews=anubis.call.allow_view_reviews(call),
        allow_view_decisions=anubis.call.allow_view_decisions(call),
        allow_view_grants=anubis.call.allow_view_grants(call),
        get_reviewer_review=anubis.review.get_reviewer_review,
    )
Example #5
0
def create(pid):
    "Create a decision for the proposal."
    proposal = anubis.proposal.get_proposal(pid)
    if proposal is None:
        return utils.error("No such proposal.", flask.url_for("home"))
    try:
        if not allow_create(proposal):
            raise ValueError("You may not create a decision for the proposal.")
        decision = get_decision(proposal.get("decision"))
        if decision is not None:
            utils.flash_message("The decision already exists.")
            return flask.redirect(
                flask.url_for(".display", iuid=decision["_id"]))
        with DecisionSaver(proposal=proposal) as saver:
            pass
        decision = saver.doc
        with anubis.proposal.ProposalSaver(proposal) as saver:
            saver["decision"] = decision["_id"]
    except ValueError as error:
        utils.flash_error(error)
    return flask.redirect(flask.url_for(".display", iuid=decision["_id"]))
Example #6
0
def get_call_xlsx(call, submitted=False, proposals=None):
    """Return the content of an XLSX file for all proposals in a call.
    Optionally only the submitted ones.
    Optionally for the given list proposals.
    """
    if proposals is None:
        title = f"Proposals in {call['identifier']}"
        proposals = get_call_proposals(call, submitted=submitted)
    else:
        title = f"Selected proposals in {call['identifier']}"
    score_fields = get_review_score_fields(call, proposals)
    rank_fields, rank_errors = get_review_rank_fields_errors(call, proposals)
    output = io.BytesIO()
    wb = xlsxwriter.Workbook(output, {"in_memory": True})
    head_text_format = wb.add_format(
        {
            "bold": True,
            "text_wrap": True,
            "bg_color": "#9ECA7F",
            "font_size": 15,
            "align": "center",
            "border": 1,
        }
    )
    normal_text_format = wb.add_format(
        {"font_size": 14, "align": "left", "valign": "vcenter"}
    )
    ws = wb.add_worksheet(title[:31])
    ws.freeze_panes(1, 1)
    ws.set_row(0, 60, head_text_format)
    ws.set_column(1, 1, 40, normal_text_format)
    ws.set_column(2, 2, 10, normal_text_format)
    ws.set_column(3, 4, 20, normal_text_format)

    nrow = 0
    row = ["Proposal", "Proposal title"]
    row.extend(["Submitted", "Submitter", "Email", "Affiliation"])
    ncol = len(row)
    for field in call["proposal"]:
        row.append(field["title"] or field["identifier"].capitalize())
        if field["type"] in (constants.LINE, constants.EMAIL):
            ws.set_column(ncol, ncol, 40, normal_text_format)
        elif field["type"] == constants.TEXT:
            ws.set_column(ncol, ncol, 60, normal_text_format)
        ncol += 1
    allow_view_reviews = anubis.call.allow_view_reviews(call)
    if allow_view_reviews:
        for rf in rank_fields.values():
            row.append(f"Reviews {rf['title']}: ranking factor")
            row.append(f"Reviews {rf['title']}: stdev")
        if len(score_fields) >= 2:
            row.append("Reviews all scores: mean of means")
            row.append("Reviews all scores: stdev of means")
        for rf in score_fields.values():
            row.append(f"Reviews {rf['title']}: N")
            row.append(f"Reviews {rf['title']}: mean")
            row.append(f"Reviews {rf['title']}: stdev")
    allow_view_decisions = anubis.call.allow_view_decisions(call)
    if allow_view_decisions:
        row.append("Decision")
        row.append("Decision status")
        for field in call["decision"]:
            if not field.get("banner"):
                continue
            title = field["title"] or field["identifier"].capitalize()
            row.append(title)
    ws.write_row(nrow, 0, row)
    nrow += 1

    for proposal in proposals:
        ncol = 0
        ws.write_url(
            nrow,
            ncol,
            flask.url_for(
                "proposal.display", pid=proposal["identifier"], _external=True
            ),
            string=proposal["identifier"],
        )
        ncol += 1
        ws.write_string(nrow, ncol, proposal.get("title") or "")
        ncol += 1
        ws.write_string(nrow, ncol, proposal.get("submitted") and "yes" or "no")
        ncol += 1
        user = anubis.user.get_user(username=proposal["user"])
        ws.write_string(nrow, ncol, utils.get_fullname(user))
        ncol += 1
        ws.write_string(nrow, ncol, user.get("email") or "")
        ncol += 1
        ws.write_string(nrow, ncol, user.get("affiliation") or "")
        ncol += 1

        for field in call["proposal"]:
            value = proposal["values"].get(field["identifier"])
            if value is None:
                ws.write_string(nrow, ncol, "")
            elif field["type"] == constants.TEXT:
                ws.write_string(nrow, ncol, value)
            elif field["type"] == constants.DOCUMENT:
                ws.write_url(
                    nrow,
                    ncol,
                    flask.url_for(
                        "proposal.document",
                        pid=proposal["identifier"],
                        fid=field["identifier"],
                        _external=True,
                    ),
                    string="Download",
                )
            elif field["type"] == constants.SELECT:
                if isinstance(value, list):  # Multiselect
                    ws.write(nrow, ncol, "\n".join(value))
                else:
                    ws.write(nrow, ncol, value)
            else:
                ws.write(nrow, ncol, value)
            ncol += 1

        if allow_view_reviews:
            for id in rank_fields.keys():
                value = proposal["ranking"][id]["factor"]
                if value is None:
                    ws.write_string(nrow, ncol, "")
                else:
                    ws.write_number(nrow, ncol, value)
                ncol += 1
                value = proposal["ranking"][id]["stdev"]
                if value is None:
                    ws.write_string(nrow, ncol, "")
                else:
                    ws.write_number(nrow, ncol, value)
                ncol += 1
            if len(score_fields) >= 2:
                value = proposal["scores"]["__mean__"]
                if value is None:
                    ws.write_string(nrow, ncol, "")
                else:
                    ws.write_number(nrow, ncol, value)
                ncol += 1
                value = proposal["scores"]["__stdev__"]
                if value is None:
                    ws.write_string(nrow, ncol, "")
                else:
                    ws.write_number(nrow, ncol, value)
                ncol += 1
            for id in score_fields:
                ws.write_number(nrow, ncol, proposal["scores"][id]["n"])
                ncol += 1
                value = proposal["scores"][id]["mean"]
                if value is None:
                    ws.write_string(nrow, ncol, "")
                else:
                    ws.write_number(nrow, ncol, value)
                ncol += 1
                value = proposal["scores"][id]["stdev"]
                if value is None:
                    ws.write_string(nrow, ncol, "")
                else:
                    ws.write_number(nrow, ncol, value)
                ncol += 1

        if allow_view_decisions:
            decision = anubis.decision.get_decision(proposal.get("decision")) or {}
            if decision:
                verdict = decision.get("verdict")
                if verdict:
                    ws.write(nrow, ncol, "Accepted")
                elif verdict is None:
                    ws.write(nrow, ncol, "Undecided")
                else:
                    ws.write(nrow, ncol, "Declined")
            else:
                ws.write(nrow, ncol, "-")
            ncol += 1
            if decision.get("finalized"):
                ws.write(nrow, ncol, "Finalized")
            else:
                ws.write(nrow, ncol, "-")
            ncol += 1
            for field in call["decision"]:
                if not field.get("banner"):
                    continue
                if decision.get("finalized"):
                    value = decision["values"].get(field["identifier"])
                    ws.write(nrow, ncol, value)
                else:
                    ws.write_string(nrow, ncol, "")
                ncol += 1

        nrow += 1

    wb.close()
    return output.getvalue()
Example #7
0
def get_reviews_xlsx(call, proposals, reviews_lookup):
    "Return the content for the XLSX file for the list of reviews."
    output = io.BytesIO()
    wb = xlsxwriter.Workbook(output, {"in_memory": True})
    head_text_format = wb.add_format({
        "bold": True,
        "text_wrap": True,
        "bg_color": "#9ECA7F",
        "font_size": 15,
        "align": "center",
        "border": 1,
    })
    normal_text_format = wb.add_format({
        "font_size": 14,
        "align": "left",
        "valign": "vcenter"
    })
    ws = wb.add_worksheet(f"Reviews in call {call['identifier']}"[:31])
    ws.freeze_panes(1, 1)
    ws.set_row(0, 60, head_text_format)
    ws.set_column(1, 1, 40, normal_text_format)
    ws.set_column(2, 2, 20, normal_text_format)
    ws.set_column(3, 3, 40, normal_text_format)

    nrow = 0
    row = ["Proposal", "Proposal title"]
    row.extend([
        "Submitter", "Email", "Affiliation", "Reviewer", "Review", "Finalized"
    ])
    ncol = len(row)
    for field in call["review"]:
        row.append(field["title"] or field["identifier"].capitalize())
        if field["type"] in (constants.LINE, constants.EMAIL):
            ws.set_column(ncol, ncol, 40, normal_text_format)
        elif field["type"] == constants.TEXT:
            ws.set_column(ncol, ncol, 60, normal_text_format)
        ncol += 1
    ws.write_row(nrow, 0, row)
    nrow += 1

    for proposal in proposals:
        for reviewer in call["reviewers"]:
            review = reviews_lookup.get("{} {}".format(proposal["identifier"],
                                                       reviewer))
            if not review:
                continue
            user = anubis.user.get_user(username=proposal["user"])
            ncol = 0
            ws.write_url(
                nrow,
                ncol,
                flask.url_for("proposal.display",
                              pid=proposal["identifier"],
                              _external=True),
                string=proposal["identifier"],
            )
            ncol += 1
            ws.write_string(nrow, ncol, proposal.get("title") or "")
            ncol += 1
            ws.write_string(nrow, ncol, utils.get_fullname(user))
            ncol += 1
            ws.write_string(nrow, ncol, user.get("email") or "")
            ncol += 1
            ws.write_string(nrow, ncol, user.get("affiliation") or "")
            ncol += 1
            ws.write_string(nrow, ncol, reviewer)
            ncol += 1
            ws.write_url(
                nrow,
                ncol,
                flask.url_for("review.display",
                              iuid=review["_id"],
                              _external=True),
                string="Link",
            )
            ncol += 1
            ws.write_string(nrow, ncol,
                            review.get("finalized") and "yes" or "no")
            ncol += 1

            for field in call["review"]:
                value = review["values"].get(field["identifier"])
                if value is None:
                    ws.write_string(nrow, ncol, "")
                elif field["type"] == constants.TEXT:
                    ws.write_string(nrow, ncol, value)
                elif field["type"] == constants.DOCUMENT:
                    ws.write_url(
                        nrow,
                        ncol,
                        flask.url_for(
                            "review.document",
                            iuid=review["_id"],
                            fid=field["identifier"],
                            _external=True,
                        ),
                        string="Download",
                    )
                else:
                    ws.write(nrow, ncol, value)
                ncol += 1
            nrow += 1

    wb.close()
    return output.getvalue()