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