workspace -> portfolio everywhere
This commit is contained in:
@@ -52,25 +52,25 @@ def home():
|
||||
if user.atat_role_name == "ccpo":
|
||||
return redirect(url_for("requests.requests_index"))
|
||||
|
||||
num_workspaces = len(user.workspace_roles)
|
||||
num_portfolios = len(user.portfolio_roles)
|
||||
|
||||
if num_workspaces == 0:
|
||||
if num_portfolios == 0:
|
||||
return redirect(url_for("requests.requests_index"))
|
||||
elif num_workspaces == 1:
|
||||
workspace_role = user.workspace_roles[0]
|
||||
workspace_id = workspace_role.workspace.id
|
||||
is_request_owner = workspace_role.role.name == "owner"
|
||||
elif num_portfolios == 1:
|
||||
portfolio_role = user.portfolio_roles[0]
|
||||
portfolio_id = portfolio_role.portfolio.id
|
||||
is_request_owner = portfolio_role.role.name == "owner"
|
||||
|
||||
if is_request_owner:
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_reports", workspace_id=workspace_id)
|
||||
url_for("portfolios.portfolio_reports", portfolio_id=portfolio_id)
|
||||
)
|
||||
else:
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_applications", workspace_id=workspace_id)
|
||||
url_for("portfolios.portfolio_applications", portfolio_id=portfolio_id)
|
||||
)
|
||||
else:
|
||||
return redirect(url_for("workspaces.workspaces"))
|
||||
return redirect(url_for("portfolios.portfolios"))
|
||||
|
||||
|
||||
@bp.route("/styleguide")
|
||||
|
@@ -8,7 +8,7 @@ from atst.domain.invitations import (
|
||||
ExpiredError as InvitationExpiredError,
|
||||
WrongUserError as InvitationWrongUserError,
|
||||
)
|
||||
from atst.domain.workspaces import WorkspaceError
|
||||
from atst.domain.portfolios import PortfolioError
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ def make_error_pages(app):
|
||||
@app.errorhandler(werkzeug_exceptions.NotFound)
|
||||
@app.errorhandler(exceptions.NotFoundError)
|
||||
@app.errorhandler(exceptions.UnauthorizedError)
|
||||
@app.errorhandler(WorkspaceError)
|
||||
@app.errorhandler(PortfolioError)
|
||||
# pylint: disable=unused-variable
|
||||
def not_found(e):
|
||||
return handle_error(e)
|
||||
|
41
atst/routes/portfolios/__init__.py
Normal file
41
atst/routes/portfolios/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from flask import Blueprint, request as http_request, g, render_template
|
||||
|
||||
portfolios_bp = Blueprint("portfolios", __name__)
|
||||
|
||||
from . import index
|
||||
from . import applications
|
||||
from . import members
|
||||
from . import invitations
|
||||
from . import task_orders
|
||||
from atst.domain.exceptions import UnauthorizedError
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.authz import Authorization
|
||||
from atst.models.permissions import Permissions
|
||||
|
||||
|
||||
@portfolios_bp.context_processor
|
||||
def portfolio():
|
||||
portfolios = Portfolios.for_user(g.current_user)
|
||||
portfolio = None
|
||||
if "portfolio_id" in http_request.view_args:
|
||||
try:
|
||||
portfolio = Portfolios.get(
|
||||
g.current_user, http_request.view_args["portfolio_id"]
|
||||
)
|
||||
portfolios = [ws for ws in portfolios if not ws.id == portfolio.id]
|
||||
except UnauthorizedError:
|
||||
pass
|
||||
|
||||
def user_can(permission):
|
||||
if portfolio:
|
||||
return Authorization.has_portfolio_permission(
|
||||
g.current_user, portfolio, permission
|
||||
)
|
||||
return False
|
||||
|
||||
return {
|
||||
"portfolio": portfolio,
|
||||
"portfolios": portfolios,
|
||||
"permissions": Permissions,
|
||||
"user_can": user_can,
|
||||
}
|
102
atst/routes/portfolios/applications.py
Normal file
102
atst/routes/portfolios/applications.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from flask import (
|
||||
current_app as app,
|
||||
g,
|
||||
redirect,
|
||||
render_template,
|
||||
request as http_request,
|
||||
url_for,
|
||||
)
|
||||
|
||||
from . import portfolios_bp
|
||||
from atst.domain.environment_roles import EnvironmentRoles
|
||||
from atst.domain.exceptions import UnauthorizedError
|
||||
from atst.domain.applications import Applications
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.forms.application import NewApplicationForm, ApplicationForm
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/applications")
|
||||
def portfolio_applications(portfolio_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
return render_template("portfolios/applications/index.html", portfolio=portfolio)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/applications/new")
|
||||
def new_application(portfolio_id):
|
||||
portfolio = Portfolios.get_for_update_applications(g.current_user, portfolio_id)
|
||||
form = NewApplicationForm()
|
||||
return render_template(
|
||||
"portfolios/applications/new.html", portfolio=portfolio, form=form
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/applications/new", methods=["POST"])
|
||||
def create_application(portfolio_id):
|
||||
portfolio = Portfolios.get_for_update_applications(g.current_user, portfolio_id)
|
||||
form = NewApplicationForm(http_request.form)
|
||||
|
||||
if form.validate():
|
||||
application_data = form.data
|
||||
Applications.create(
|
||||
g.current_user,
|
||||
portfolio,
|
||||
application_data["name"],
|
||||
application_data["description"],
|
||||
application_data["environment_names"],
|
||||
)
|
||||
return redirect(
|
||||
url_for("portfolios.portfolio_applications", portfolio_id=portfolio.id)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"portfolios/applications/new.html", portfolio=portfolio, form=form
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/applications/<application_id>/edit")
|
||||
def edit_application(portfolio_id, application_id):
|
||||
portfolio = Portfolios.get_for_update_applications(g.current_user, portfolio_id)
|
||||
application = Applications.get(g.current_user, portfolio, application_id)
|
||||
form = ApplicationForm(name=application.name, description=application.description)
|
||||
|
||||
return render_template(
|
||||
"portfolios/applications/edit.html",
|
||||
portfolio=portfolio,
|
||||
application=application,
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route(
|
||||
"/portfolios/<portfolio_id>/applications/<application_id>/edit", methods=["POST"]
|
||||
)
|
||||
def update_application(portfolio_id, application_id):
|
||||
portfolio = Portfolios.get_for_update_applications(g.current_user, portfolio_id)
|
||||
application = Applications.get(g.current_user, portfolio, application_id)
|
||||
form = ApplicationForm(http_request.form)
|
||||
if form.validate():
|
||||
application_data = form.data
|
||||
Applications.update(g.current_user, portfolio, application, application_data)
|
||||
|
||||
return redirect(
|
||||
url_for("portfolios.portfolio_applications", portfolio_id=portfolio.id)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"portfolios/applications/edit.html",
|
||||
portfolio=portfolio,
|
||||
application=application,
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/environments/<environment_id>/access")
|
||||
def access_environment(portfolio_id, environment_id):
|
||||
env_role = EnvironmentRoles.get(g.current_user.id, environment_id)
|
||||
if not env_role:
|
||||
raise UnauthorizedError(
|
||||
g.current_user, "access environment {}".format(environment_id)
|
||||
)
|
||||
else:
|
||||
token = app.csp.cloud.get_access_token(env_role)
|
||||
return redirect(url_for("atst.csp_environment_access", token=token))
|
102
atst/routes/portfolios/index.py
Normal file
102
atst/routes/portfolios/index.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from datetime import date, timedelta
|
||||
|
||||
from flask import render_template, request as http_request, g, redirect, url_for
|
||||
|
||||
from . import portfolios_bp
|
||||
from atst.domain.reports import Reports
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.audit_log import AuditLog
|
||||
from atst.domain.authz import Authorization
|
||||
from atst.domain.common import Paginator
|
||||
from atst.forms.portfolio import PortfolioForm
|
||||
from atst.models.permissions import Permissions
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios")
|
||||
def portfolios():
|
||||
portfolios = Portfolios.for_user(g.current_user)
|
||||
return render_template("portfolios/index.html", page=5, portfolios=portfolios)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/edit")
|
||||
def portfolio(portfolio_id):
|
||||
portfolio = Portfolios.get_for_update_information(g.current_user, portfolio_id)
|
||||
form = PortfolioForm(data={"name": portfolio.name})
|
||||
return render_template("portfolios/edit.html", form=form, portfolio=portfolio)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/edit", methods=["POST"])
|
||||
def edit_portfolio(portfolio_id):
|
||||
portfolio = Portfolios.get_for_update_information(g.current_user, portfolio_id)
|
||||
form = PortfolioForm(http_request.form)
|
||||
if form.validate():
|
||||
Portfolios.update(portfolio, form.data)
|
||||
return redirect(
|
||||
url_for("portfolios.portfolio_applications", portfolio_id=portfolio.id)
|
||||
)
|
||||
else:
|
||||
return render_template("portfolios/edit.html", form=form, portfolio=portfolio)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>")
|
||||
def show_portfolio(portfolio_id):
|
||||
return redirect(
|
||||
url_for("portfolios.portfolio_applications", portfolio_id=portfolio_id)
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/reports")
|
||||
def portfolio_reports(portfolio_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
Authorization.check_portfolio_permission(
|
||||
g.current_user,
|
||||
portfolio,
|
||||
Permissions.VIEW_USAGE_DOLLARS,
|
||||
"view portfolio reports",
|
||||
)
|
||||
|
||||
today = date.today()
|
||||
month = http_request.args.get("month", today.month)
|
||||
year = http_request.args.get("year", today.year)
|
||||
current_month = date(int(year), int(month), 15)
|
||||
prev_month = current_month - timedelta(days=28)
|
||||
two_months_ago = prev_month - timedelta(days=28)
|
||||
|
||||
expiration_date = (
|
||||
portfolio.legacy_task_order and portfolio.legacy_task_order.expiration_date
|
||||
)
|
||||
if expiration_date:
|
||||
remaining_difference = expiration_date - today
|
||||
remaining_days = remaining_difference.days
|
||||
else:
|
||||
remaining_days = None
|
||||
|
||||
return render_template(
|
||||
"portfolios/reports/index.html",
|
||||
cumulative_budget=Reports.cumulative_budget(portfolio),
|
||||
portfolio_totals=Reports.portfolio_totals(portfolio),
|
||||
monthly_totals=Reports.monthly_totals(portfolio),
|
||||
jedi_request=portfolio.request,
|
||||
legacy_task_order=portfolio.legacy_task_order,
|
||||
current_month=current_month,
|
||||
prev_month=prev_month,
|
||||
two_months_ago=two_months_ago,
|
||||
expiration_date=expiration_date,
|
||||
remaining_days=remaining_days,
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/activity")
|
||||
def portfolio_activity(portfolio_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
pagination_opts = Paginator.get_pagination_opts(http_request)
|
||||
audit_events = AuditLog.get_portfolio_events(
|
||||
g.current_user, portfolio, pagination_opts
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"portfolios/activity/index.html",
|
||||
portfolio_name=portfolio.name,
|
||||
portfolio_id=portfolio_id,
|
||||
audit_events=audit_events,
|
||||
)
|
@@ -1,7 +1,7 @@
|
||||
from flask import g, redirect, url_for, render_template
|
||||
|
||||
from . import workspaces_bp
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from . import portfolios_bp
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.invitations import Invitations
|
||||
from atst.queue import queue
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
@@ -11,12 +11,12 @@ def send_invite_email(owner_name, token, new_member_email):
|
||||
body = render_template("emails/invitation.txt", owner=owner_name, token=token)
|
||||
queue.send_mail(
|
||||
[new_member_email],
|
||||
"{} has invited you to a JEDI Cloud Workspace".format(owner_name),
|
||||
"{} has invited you to a JEDI Cloud Portfolio".format(owner_name),
|
||||
body,
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/invitations/<token>", methods=["GET"])
|
||||
@portfolios_bp.route("/portfolios/invitations/<token>", methods=["GET"])
|
||||
def accept_invitation(token):
|
||||
invite = Invitations.accept(g.current_user, token)
|
||||
|
||||
@@ -25,7 +25,7 @@ def accept_invitation(token):
|
||||
# are. It will also have to manage cases like:
|
||||
# - the logged-in user has multiple roles on the TO (e.g., KO and COR)
|
||||
# - the logged-in user has officer roles on multiple unsigned TOs
|
||||
for task_order in invite.workspace.task_orders:
|
||||
for task_order in invite.portfolio.task_orders:
|
||||
if g.current_user == task_order.contracting_officer:
|
||||
return redirect(
|
||||
url_for("task_orders.new", screen=4, task_order_id=task_order.id)
|
||||
@@ -40,25 +40,25 @@ def accept_invitation(token):
|
||||
)
|
||||
|
||||
return redirect(
|
||||
url_for("workspaces.show_workspace", workspace_id=invite.workspace.id)
|
||||
url_for("portfolios.show_portfolio", portfolio_id=invite.portfolio.id)
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route(
|
||||
"/workspaces/<workspace_id>/invitations/<token>/revoke", methods=["POST"]
|
||||
@portfolios_bp.route(
|
||||
"/portfolios/<portfolio_id>/invitations/<token>/revoke", methods=["POST"]
|
||||
)
|
||||
def revoke_invitation(workspace_id, token):
|
||||
workspace = Workspaces.get_for_update_member(g.current_user, workspace_id)
|
||||
def revoke_invitation(portfolio_id, token):
|
||||
portfolio = Portfolios.get_for_update_member(g.current_user, portfolio_id)
|
||||
Invitations.revoke(token)
|
||||
|
||||
return redirect(url_for("workspaces.workspace_members", workspace_id=workspace.id))
|
||||
return redirect(url_for("portfolios.portfolio_members", portfolio_id=portfolio.id))
|
||||
|
||||
|
||||
@workspaces_bp.route(
|
||||
"/workspaces/<workspace_id>/invitations/<token>/resend", methods=["POST"]
|
||||
@portfolios_bp.route(
|
||||
"/portfolios/<portfolio_id>/invitations/<token>/resend", methods=["POST"]
|
||||
)
|
||||
def resend_invitation(workspace_id, token):
|
||||
invite = Invitations.resend(g.current_user, workspace_id, token)
|
||||
def resend_invitation(portfolio_id, token):
|
||||
invite = Invitations.resend(g.current_user, portfolio_id, token)
|
||||
send_invite_email(g.current_user.full_name, invite.token, invite.email)
|
||||
flash("resend_workspace_invitation", user_name=invite.user_name)
|
||||
return redirect(url_for("workspaces.workspace_members", workspace_id=workspace_id))
|
||||
flash("resend_portfolio_invitation", user_name=invite.user_name)
|
||||
return redirect(url_for("portfolios.portfolio_members", portfolio_id=portfolio_id))
|
@@ -2,11 +2,11 @@ import re
|
||||
|
||||
from flask import render_template, request as http_request, g, redirect, url_for
|
||||
|
||||
from . import workspaces_bp
|
||||
from . import portfolios_bp
|
||||
from atst.domain.exceptions import AlreadyExistsError
|
||||
from atst.domain.applications import Applications
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from atst.domain.workspace_roles import WorkspaceRoles, MEMBER_STATUS_CHOICES
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.portfolio_roles import PortfolioRoles, MEMBER_STATUS_CHOICES
|
||||
from atst.domain.environments import Environments
|
||||
from atst.domain.environment_roles import EnvironmentRoles
|
||||
from atst.services.invitation import Invitation as InvitationService
|
||||
@@ -23,12 +23,12 @@ from atst.models.permissions import Permissions
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/members")
|
||||
def workspace_members(workspace_id):
|
||||
workspace = Workspaces.get_with_members(g.current_user, workspace_id)
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/members")
|
||||
def portfolio_members(portfolio_id):
|
||||
portfolio = Portfolios.get_with_members(g.current_user, portfolio_id)
|
||||
new_member_name = http_request.args.get("newMemberName")
|
||||
new_member = next(
|
||||
filter(lambda m: m.user_name == new_member_name, workspace.members), None
|
||||
filter(lambda m: m.user_name == new_member_name, portfolio.members), None
|
||||
)
|
||||
members_list = [
|
||||
{
|
||||
@@ -38,15 +38,15 @@ def workspace_members(workspace_id):
|
||||
"role": k.role_displayname,
|
||||
"num_env": k.num_environment_roles,
|
||||
"edit_link": url_for(
|
||||
"workspaces.view_member", workspace_id=workspace.id, member_id=k.user_id
|
||||
"portfolios.view_member", portfolio_id=portfolio.id, member_id=k.user_id
|
||||
),
|
||||
}
|
||||
for k in workspace.members
|
||||
for k in portfolio.members
|
||||
]
|
||||
|
||||
return render_template(
|
||||
"workspaces/members/index.html",
|
||||
workspace=workspace,
|
||||
"portfolios/members/index.html",
|
||||
portfolio=portfolio,
|
||||
role_choices=WORKSPACE_ROLE_DEFINITIONS,
|
||||
status_choices=MEMBER_STATUS_CHOICES,
|
||||
members=members_list,
|
||||
@@ -54,32 +54,32 @@ def workspace_members(workspace_id):
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/members/new")
|
||||
def new_member(workspace_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/members/new")
|
||||
def new_member(portfolio_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
form = NewMemberForm()
|
||||
return render_template(
|
||||
"workspaces/members/new.html", workspace=workspace, form=form
|
||||
"portfolios/members/new.html", portfolio=portfolio, form=form
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/members/new", methods=["POST"])
|
||||
def create_member(workspace_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/members/new", methods=["POST"])
|
||||
def create_member(portfolio_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
form = NewMemberForm(http_request.form)
|
||||
|
||||
if form.validate():
|
||||
try:
|
||||
member = Workspaces.create_member(g.current_user, workspace, form.data)
|
||||
member = Portfolios.create_member(g.current_user, portfolio, form.data)
|
||||
invite_service = InvitationService(
|
||||
g.current_user, member, form.data.get("email")
|
||||
)
|
||||
invite_service.invite()
|
||||
|
||||
flash("new_workspace_member", new_member=new_member, workspace=workspace)
|
||||
flash("new_portfolio_member", new_member=new_member, portfolio=portfolio)
|
||||
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_members", workspace_id=workspace.id)
|
||||
url_for("portfolios.portfolio_members", portfolio_id=portfolio.id)
|
||||
)
|
||||
except AlreadyExistsError:
|
||||
return render_template(
|
||||
@@ -87,31 +87,31 @@ def create_member(workspace_id):
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"workspaces/members/new.html", workspace=workspace, form=form
|
||||
"portfolios/members/new.html", portfolio=portfolio, form=form
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/members/<member_id>/member_edit")
|
||||
def view_member(workspace_id, member_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
Authorization.check_workspace_permission(
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/members/<member_id>/member_edit")
|
||||
def view_member(portfolio_id, member_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
Authorization.check_portfolio_permission(
|
||||
g.current_user,
|
||||
workspace,
|
||||
portfolio,
|
||||
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
|
||||
"edit this workspace user",
|
||||
"edit this portfolio user",
|
||||
)
|
||||
member = WorkspaceRoles.get(workspace_id, member_id)
|
||||
applications = Applications.get_all(g.current_user, member, workspace)
|
||||
form = EditMemberForm(workspace_role=member.role_name)
|
||||
member = PortfolioRoles.get(portfolio_id, member_id)
|
||||
applications = Applications.get_all(g.current_user, member, portfolio)
|
||||
form = EditMemberForm(portfolio_role=member.role_name)
|
||||
editable = g.current_user == member.user
|
||||
can_revoke_access = Workspaces.can_revoke_access_for(workspace, member)
|
||||
can_revoke_access = Portfolios.can_revoke_access_for(portfolio, member)
|
||||
|
||||
if member.has_dod_id_error:
|
||||
flash("workspace_member_dod_id_error")
|
||||
flash("portfolio_member_dod_id_error")
|
||||
|
||||
return render_template(
|
||||
"workspaces/members/edit.html",
|
||||
workspace=workspace,
|
||||
"portfolios/members/edit.html",
|
||||
portfolio=portfolio,
|
||||
member=member,
|
||||
applications=applications,
|
||||
form=form,
|
||||
@@ -123,18 +123,18 @@ def view_member(workspace_id, member_id):
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route(
|
||||
"/workspaces/<workspace_id>/members/<member_id>/member_edit", methods=["POST"]
|
||||
@portfolios_bp.route(
|
||||
"/portfolios/<portfolio_id>/members/<member_id>/member_edit", methods=["POST"]
|
||||
)
|
||||
def update_member(workspace_id, member_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
Authorization.check_workspace_permission(
|
||||
def update_member(portfolio_id, member_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
Authorization.check_portfolio_permission(
|
||||
g.current_user,
|
||||
workspace,
|
||||
portfolio,
|
||||
Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE,
|
||||
"edit this workspace user",
|
||||
"edit this portfolio user",
|
||||
)
|
||||
member = WorkspaceRoles.get(workspace_id, member_id)
|
||||
member = PortfolioRoles.get(portfolio_id, member_id)
|
||||
|
||||
ids_and_roles = []
|
||||
form_dict = http_request.form.to_dict()
|
||||
@@ -147,39 +147,39 @@ def update_member(workspace_id, member_id):
|
||||
form = EditMemberForm(http_request.form)
|
||||
if form.validate():
|
||||
new_role_name = None
|
||||
if form.data["workspace_role"] != member.role.name:
|
||||
member = Workspaces.update_member(
|
||||
g.current_user, workspace, member, form.data["workspace_role"]
|
||||
if form.data["portfolio_role"] != member.role.name:
|
||||
member = Portfolios.update_member(
|
||||
g.current_user, portfolio, member, form.data["portfolio_role"]
|
||||
)
|
||||
new_role_name = member.role_displayname
|
||||
flash(
|
||||
"workspace_role_updated",
|
||||
"portfolio_role_updated",
|
||||
member_name=member.user_name,
|
||||
updated_role=new_role_name,
|
||||
)
|
||||
|
||||
updated_roles = Environments.update_environment_roles(
|
||||
g.current_user, workspace, member, ids_and_roles
|
||||
g.current_user, portfolio, member, ids_and_roles
|
||||
)
|
||||
if updated_roles:
|
||||
flash("environment_access_changed")
|
||||
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_members", workspace_id=workspace.id)
|
||||
url_for("portfolios.portfolio_members", portfolio_id=portfolio.id)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"workspaces/members/edit.html",
|
||||
"portfolios/members/edit.html",
|
||||
form=form,
|
||||
workspace=workspace,
|
||||
portfolio=portfolio,
|
||||
member=member,
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route(
|
||||
"/workspaces/<workspace_id>/members/<member_id>/revoke_access", methods=["POST"]
|
||||
@portfolios_bp.route(
|
||||
"/portfolios/<portfolio_id>/members/<member_id>/revoke_access", methods=["POST"]
|
||||
)
|
||||
def revoke_access(workspace_id, member_id):
|
||||
revoked_role = Workspaces.revoke_access(g.current_user, workspace_id, member_id)
|
||||
flash("revoked_workspace_access", member_name=revoked_role.user.full_name)
|
||||
return redirect(url_for("workspaces.workspace_members", workspace_id=workspace_id))
|
||||
def revoke_access(portfolio_id, member_id):
|
||||
revoked_role = Portfolios.revoke_access(g.current_user, portfolio_id, member_id)
|
||||
flash("revoked_portfolio_access", member_name=revoked_role.user.full_name)
|
||||
return redirect(url_for("portfolios.portfolio_members", portfolio_id=portfolio_id))
|
20
atst/routes/portfolios/task_orders.py
Normal file
20
atst/routes/portfolios/task_orders.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from flask import g, render_template
|
||||
|
||||
from . import portfolios_bp
|
||||
from atst.domain.task_orders import TaskOrders
|
||||
from atst.domain.portfolios import Portfolios
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/task_orders")
|
||||
def portfolio_task_orders(portfolio_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
return render_template("portfolios/task_orders/index.html", portfolio=portfolio)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/task_order/<task_order_id>")
|
||||
def view_task_order(portfolio_id, task_order_id):
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
task_order = TaskOrders.get(task_order_id)
|
||||
return render_template(
|
||||
"portfolios/task_orders/show.html", portfolio=portfolio, task_order=task_order
|
||||
)
|
@@ -248,10 +248,10 @@ def update_financial_verification(request_id):
|
||||
)
|
||||
|
||||
if updated_request.legacy_task_order.verified:
|
||||
workspace = Requests.auto_approve_and_create_workspace(updated_request)
|
||||
flash("new_workspace")
|
||||
portfolio = Requests.auto_approve_and_create_portfolio(updated_request)
|
||||
flash("new_portfolio")
|
||||
return redirect(
|
||||
url_for("workspaces.new_application", workspace_id=workspace.id)
|
||||
url_for("portfolios.new_application", portfolio_id=portfolio.id)
|
||||
)
|
||||
else:
|
||||
return redirect(url_for("requests.requests_index", modal="pendingCCPOApproval"))
|
||||
|
@@ -63,10 +63,10 @@ class RequestsIndex(object):
|
||||
"extended_view": False,
|
||||
}
|
||||
|
||||
def _workspace_link_for_request(self, request):
|
||||
def _portfolio_link_for_request(self, request):
|
||||
if request.is_approved:
|
||||
return url_for(
|
||||
"workspaces.workspace_applications", workspace_id=request.workspace.id
|
||||
"portfolios.portfolio_applications", portfolio_id=request.portfolio.id
|
||||
)
|
||||
else:
|
||||
return None
|
||||
@@ -80,7 +80,7 @@ class RequestsIndex(object):
|
||||
annual_usage = request.annual_spend
|
||||
|
||||
return {
|
||||
"workspace_id": request.workspace.id if request.workspace else None,
|
||||
"portfolio_id": request.portfolio.id if request.portfolio else None,
|
||||
"name": request.displayname,
|
||||
"is_new": is_new,
|
||||
"is_approved": request.is_approved,
|
||||
@@ -93,7 +93,7 @@ class RequestsIndex(object):
|
||||
"edit_link": url_for("requests.edit", request_id=request.id),
|
||||
"action_required": request.action_required_by == viewing_role,
|
||||
"dod_component": request.latest_revision.dod_component,
|
||||
"workspace_link": self._workspace_link_for_request(request),
|
||||
"portfolio_link": self._portfolio_link_for_request(request),
|
||||
}
|
||||
|
||||
|
||||
|
@@ -113,9 +113,9 @@ class JEDIRequestFlow(object):
|
||||
"form": request_forms.InformationAboutYouForm,
|
||||
},
|
||||
{
|
||||
"title": "Workspace Owner",
|
||||
"title": "Portfolio Owner",
|
||||
"section": "primary_poc",
|
||||
"form": request_forms.WorkspaceOwnerForm,
|
||||
"form": request_forms.PortfolioOwnerForm,
|
||||
},
|
||||
{
|
||||
"title": "Review & Submit",
|
||||
|
@@ -10,5 +10,5 @@ def invite(task_order_id):
|
||||
task_order = TaskOrders.get(g.current_user, task_order_id)
|
||||
flash("task_order_submitted", task_order=task_order)
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_members", workspace_id=task_order.workspace.id)
|
||||
url_for("portfolios.portfolio_members", portfolio_id=task_order.portfolio.id)
|
||||
)
|
||||
|
@@ -11,8 +11,8 @@ from flask import (
|
||||
|
||||
from . import task_orders_bp
|
||||
from atst.domain.task_orders import TaskOrders
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from atst.domain.workspace_roles import WorkspaceRoles
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.portfolio_roles import PortfolioRoles
|
||||
import atst.forms.task_order as task_order_form
|
||||
from atst.services.invitation import Invitation as InvitationService
|
||||
|
||||
@@ -114,9 +114,9 @@ class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow):
|
||||
return self._form
|
||||
|
||||
@property
|
||||
def workspace(self):
|
||||
def portfolio(self):
|
||||
if self.task_order:
|
||||
return self.task_order.workspace
|
||||
return self.task_order.portfolio
|
||||
|
||||
def validate(self):
|
||||
return self.form.validate()
|
||||
@@ -125,7 +125,7 @@ class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow):
|
||||
if self.task_order:
|
||||
TaskOrders.update(self.user, self.task_order, **self.form.data)
|
||||
else:
|
||||
ws = Workspaces.create(self.user, self.form.portfolio_name.data)
|
||||
ws = Portfolios.create(self.user, self.form.portfolio_name.data)
|
||||
to_data = self.form.data.copy()
|
||||
to_data.pop("portfolio_name")
|
||||
self._task_order = TaskOrders.create(self.user, ws)
|
||||
@@ -177,7 +177,7 @@ class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow):
|
||||
officer = TaskOrders.add_officer(
|
||||
self.user, self.task_order, officer_type["role"], officer_data
|
||||
)
|
||||
ws_officer_member = WorkspaceRoles.get(self.workspace.id, officer.id)
|
||||
ws_officer_member = PortfolioRoles.get(self.portfolio.id, officer.id)
|
||||
invite_service = InvitationService(
|
||||
self.user,
|
||||
ws_officer_member,
|
||||
|
@@ -1,41 +0,0 @@
|
||||
from flask import Blueprint, request as http_request, g, render_template
|
||||
|
||||
workspaces_bp = Blueprint("workspaces", __name__)
|
||||
|
||||
from . import index
|
||||
from . import applications
|
||||
from . import members
|
||||
from . import invitations
|
||||
from . import task_orders
|
||||
from atst.domain.exceptions import UnauthorizedError
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from atst.domain.authz import Authorization
|
||||
from atst.models.permissions import Permissions
|
||||
|
||||
|
||||
@workspaces_bp.context_processor
|
||||
def workspace():
|
||||
workspaces = Workspaces.for_user(g.current_user)
|
||||
workspace = None
|
||||
if "workspace_id" in http_request.view_args:
|
||||
try:
|
||||
workspace = Workspaces.get(
|
||||
g.current_user, http_request.view_args["workspace_id"]
|
||||
)
|
||||
workspaces = [ws for ws in workspaces if not ws.id == workspace.id]
|
||||
except UnauthorizedError:
|
||||
pass
|
||||
|
||||
def user_can(permission):
|
||||
if workspace:
|
||||
return Authorization.has_workspace_permission(
|
||||
g.current_user, workspace, permission
|
||||
)
|
||||
return False
|
||||
|
||||
return {
|
||||
"workspace": workspace,
|
||||
"workspaces": workspaces,
|
||||
"permissions": Permissions,
|
||||
"user_can": user_can,
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
from flask import (
|
||||
current_app as app,
|
||||
g,
|
||||
redirect,
|
||||
render_template,
|
||||
request as http_request,
|
||||
url_for,
|
||||
)
|
||||
|
||||
from . import workspaces_bp
|
||||
from atst.domain.environment_roles import EnvironmentRoles
|
||||
from atst.domain.exceptions import UnauthorizedError
|
||||
from atst.domain.applications import Applications
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from atst.forms.application import NewApplicationForm, ApplicationForm
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/applications")
|
||||
def workspace_applications(workspace_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
return render_template("workspaces/applications/index.html", workspace=workspace)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/applications/new")
|
||||
def new_application(workspace_id):
|
||||
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
|
||||
form = NewApplicationForm()
|
||||
return render_template(
|
||||
"workspaces/applications/new.html", workspace=workspace, form=form
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/applications/new", methods=["POST"])
|
||||
def create_application(workspace_id):
|
||||
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
|
||||
form = NewApplicationForm(http_request.form)
|
||||
|
||||
if form.validate():
|
||||
application_data = form.data
|
||||
Applications.create(
|
||||
g.current_user,
|
||||
workspace,
|
||||
application_data["name"],
|
||||
application_data["description"],
|
||||
application_data["environment_names"],
|
||||
)
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_applications", workspace_id=workspace.id)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"workspaces/applications/new.html", workspace=workspace, form=form
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/applications/<application_id>/edit")
|
||||
def edit_application(workspace_id, application_id):
|
||||
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
|
||||
application = Applications.get(g.current_user, workspace, application_id)
|
||||
form = ApplicationForm(name=application.name, description=application.description)
|
||||
|
||||
return render_template(
|
||||
"workspaces/applications/edit.html",
|
||||
workspace=workspace,
|
||||
application=application,
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route(
|
||||
"/workspaces/<workspace_id>/applications/<application_id>/edit", methods=["POST"]
|
||||
)
|
||||
def update_application(workspace_id, application_id):
|
||||
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
|
||||
application = Applications.get(g.current_user, workspace, application_id)
|
||||
form = ApplicationForm(http_request.form)
|
||||
if form.validate():
|
||||
application_data = form.data
|
||||
Applications.update(g.current_user, workspace, application, application_data)
|
||||
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_applications", workspace_id=workspace.id)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"workspaces/applications/edit.html",
|
||||
workspace=workspace,
|
||||
application=application,
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/environments/<environment_id>/access")
|
||||
def access_environment(workspace_id, environment_id):
|
||||
env_role = EnvironmentRoles.get(g.current_user.id, environment_id)
|
||||
if not env_role:
|
||||
raise UnauthorizedError(
|
||||
g.current_user, "access environment {}".format(environment_id)
|
||||
)
|
||||
else:
|
||||
token = app.csp.cloud.get_access_token(env_role)
|
||||
return redirect(url_for("atst.csp_environment_access", token=token))
|
@@ -1,102 +0,0 @@
|
||||
from datetime import date, timedelta
|
||||
|
||||
from flask import render_template, request as http_request, g, redirect, url_for
|
||||
|
||||
from . import workspaces_bp
|
||||
from atst.domain.reports import Reports
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from atst.domain.audit_log import AuditLog
|
||||
from atst.domain.authz import Authorization
|
||||
from atst.domain.common import Paginator
|
||||
from atst.forms.workspace import WorkspaceForm
|
||||
from atst.models.permissions import Permissions
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces")
|
||||
def workspaces():
|
||||
workspaces = Workspaces.for_user(g.current_user)
|
||||
return render_template("workspaces/index.html", page=5, workspaces=workspaces)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/edit")
|
||||
def workspace(workspace_id):
|
||||
workspace = Workspaces.get_for_update_information(g.current_user, workspace_id)
|
||||
form = WorkspaceForm(data={"name": workspace.name})
|
||||
return render_template("workspaces/edit.html", form=form, workspace=workspace)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/edit", methods=["POST"])
|
||||
def edit_workspace(workspace_id):
|
||||
workspace = Workspaces.get_for_update_information(g.current_user, workspace_id)
|
||||
form = WorkspaceForm(http_request.form)
|
||||
if form.validate():
|
||||
Workspaces.update(workspace, form.data)
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_applications", workspace_id=workspace.id)
|
||||
)
|
||||
else:
|
||||
return render_template("workspaces/edit.html", form=form, workspace=workspace)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>")
|
||||
def show_workspace(workspace_id):
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_applications", workspace_id=workspace_id)
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/reports")
|
||||
def workspace_reports(workspace_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
Authorization.check_workspace_permission(
|
||||
g.current_user,
|
||||
workspace,
|
||||
Permissions.VIEW_USAGE_DOLLARS,
|
||||
"view workspace reports",
|
||||
)
|
||||
|
||||
today = date.today()
|
||||
month = http_request.args.get("month", today.month)
|
||||
year = http_request.args.get("year", today.year)
|
||||
current_month = date(int(year), int(month), 15)
|
||||
prev_month = current_month - timedelta(days=28)
|
||||
two_months_ago = prev_month - timedelta(days=28)
|
||||
|
||||
expiration_date = (
|
||||
workspace.legacy_task_order and workspace.legacy_task_order.expiration_date
|
||||
)
|
||||
if expiration_date:
|
||||
remaining_difference = expiration_date - today
|
||||
remaining_days = remaining_difference.days
|
||||
else:
|
||||
remaining_days = None
|
||||
|
||||
return render_template(
|
||||
"workspaces/reports/index.html",
|
||||
cumulative_budget=Reports.cumulative_budget(workspace),
|
||||
workspace_totals=Reports.workspace_totals(workspace),
|
||||
monthly_totals=Reports.monthly_totals(workspace),
|
||||
jedi_request=workspace.request,
|
||||
legacy_task_order=workspace.legacy_task_order,
|
||||
current_month=current_month,
|
||||
prev_month=prev_month,
|
||||
two_months_ago=two_months_ago,
|
||||
expiration_date=expiration_date,
|
||||
remaining_days=remaining_days,
|
||||
)
|
||||
|
||||
|
||||
@workspaces_bp.route("/workspaces/<workspace_id>/activity")
|
||||
def workspace_activity(workspace_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
pagination_opts = Paginator.get_pagination_opts(http_request)
|
||||
audit_events = AuditLog.get_workspace_events(
|
||||
g.current_user, workspace, pagination_opts
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"workspaces/activity/index.html",
|
||||
workspace_name=workspace.name,
|
||||
workspace_id=workspace_id,
|
||||
audit_events=audit_events,
|
||||
)
|
Reference in New Issue
Block a user