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
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
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
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, )
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"]))
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()
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()