Merge branch 'staging' into state-machine-error-handling
This commit is contained in:
commit
b7d044b3b1
@ -103,7 +103,7 @@ commands:
|
|||||||
--password $AZURE_SP_PASSWORD \
|
--password $AZURE_SP_PASSWORD \
|
||||||
--username $AZURE_SP
|
--username $AZURE_SP
|
||||||
echo "Successfully logged in to Azure CLI."
|
echo "Successfully logged in to Azure CLI."
|
||||||
az acr login --name $AZURE_REGISTRY
|
az acr login --name $AZURE_REGISTRY | grep "Succeeded"
|
||||||
- run:
|
- run:
|
||||||
name: Install kubectl
|
name: Install kubectl
|
||||||
command: |
|
command: |
|
||||||
|
10
atst/app.py
10
atst/app.py
@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from datetime import datetime
|
import pendulum
|
||||||
from flask import Flask, request, g, session, url_for as flask_url_for
|
from flask import Flask, request, g, session, url_for as flask_url_for
|
||||||
from flask_session import Session
|
from flask_session import Session
|
||||||
import redis
|
import redis
|
||||||
@ -187,11 +187,11 @@ def map_config(config):
|
|||||||
"CELERY_RESULT_EXPIRES": 0,
|
"CELERY_RESULT_EXPIRES": 0,
|
||||||
"CELERY_RESULT_EXTENDED": True,
|
"CELERY_RESULT_EXTENDED": True,
|
||||||
"OFFICE_365_DOMAIN": "onmicrosoft.com",
|
"OFFICE_365_DOMAIN": "onmicrosoft.com",
|
||||||
"CONTRACT_START_DATE": datetime.strptime(
|
"CONTRACT_START_DATE": pendulum.from_format(
|
||||||
config.get("default", "CONTRACT_START_DATE"), "%Y-%m-%d"
|
config.get("default", "CONTRACT_START_DATE"), "YYYY-MM-DD"
|
||||||
).date(),
|
).date(),
|
||||||
"CONTRACT_END_DATE": datetime.strptime(
|
"CONTRACT_END_DATE": pendulum.from_format(
|
||||||
config.get("default", "CONTRACT_END_DATE"), "%Y-%m-%d"
|
config.get("default", "CONTRACT_END_DATE"), "YYYY-MM-DD"
|
||||||
).date(),
|
).date(),
|
||||||
"SESSION_COOKIE_SECURE": config.getboolean("default", "SESSION_COOKIE_SECURE"),
|
"SESSION_COOKIE_SECURE": config.getboolean("default", "SESSION_COOKIE_SECURE"),
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import hashlib
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from OpenSSL import crypto, SSL
|
from OpenSSL import crypto, SSL
|
||||||
from datetime import datetime
|
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
|
|
||||||
from .util import load_crl_locations_cache, serialize_crl_locations_cache, CRL_LIST
|
from .util import load_crl_locations_cache, serialize_crl_locations_cache, CRL_LIST
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from datetime import datetime, timedelta
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
import pendulum
|
||||||
|
|
||||||
|
|
||||||
class FileService:
|
class FileService:
|
||||||
@ -39,7 +39,7 @@ class AzureFileService(FileService):
|
|||||||
self.account_name = config["AZURE_ACCOUNT_NAME"]
|
self.account_name = config["AZURE_ACCOUNT_NAME"]
|
||||||
self.storage_key = config["AZURE_STORAGE_KEY"]
|
self.storage_key = config["AZURE_STORAGE_KEY"]
|
||||||
self.container_name = config["AZURE_TO_BUCKET_NAME"]
|
self.container_name = config["AZURE_TO_BUCKET_NAME"]
|
||||||
self.timeout = timedelta(seconds=config["PERMANENT_SESSION_LIFETIME"])
|
self.timeout = config["PERMANENT_SESSION_LIFETIME"]
|
||||||
|
|
||||||
from azure.storage.common import CloudStorageAccount
|
from azure.storage.common import CloudStorageAccount
|
||||||
from azure.storage.blob import BlobSasPermissions
|
from azure.storage.blob import BlobSasPermissions
|
||||||
@ -68,7 +68,7 @@ class AzureFileService(FileService):
|
|||||||
self.container_name,
|
self.container_name,
|
||||||
object_name,
|
object_name,
|
||||||
permission=self.BlobSasPermissions(create=True),
|
permission=self.BlobSasPermissions(create=True),
|
||||||
expiry=datetime.utcnow() + self.timeout,
|
expiry=pendulum.now(tz="utc").add(self.timeout),
|
||||||
protocol="https",
|
protocol="https",
|
||||||
)
|
)
|
||||||
return ({"token": sas_token}, object_name)
|
return ({"token": sas_token}, object_name)
|
||||||
@ -81,7 +81,7 @@ class AzureFileService(FileService):
|
|||||||
container_name=self.container_name,
|
container_name=self.container_name,
|
||||||
blob_name=object_name,
|
blob_name=object_name,
|
||||||
permission=self.BlobPermissions(read=True),
|
permission=self.BlobPermissions(read=True),
|
||||||
expiry=datetime.utcnow() + self.timeout,
|
expiry=pendulum.now(tz="utc").add(self.timeout),
|
||||||
content_disposition=f"attachment; filename={filename}",
|
content_disposition=f"attachment; filename={filename}",
|
||||||
protocol="https",
|
protocol="https",
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import datetime
|
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
import pendulum
|
||||||
|
|
||||||
from atst.database import db
|
from atst.database import db
|
||||||
from atst.models import ApplicationInvitation, InvitationStatus, PortfolioInvitation
|
from atst.models import ApplicationInvitation, InvitationStatus, PortfolioInvitation
|
||||||
@ -99,9 +99,7 @@ class BaseInvitations(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def current_expiration_time(cls):
|
def current_expiration_time(cls):
|
||||||
return datetime.datetime.now() + datetime.timedelta(
|
return pendulum.now(tz="utc").add(minutes=cls.EXPIRATION_LIMIT_MINUTES)
|
||||||
minutes=cls.EXPIRATION_LIMIT_MINUTES
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _update_status(cls, invite, new_status):
|
def _update_status(cls, invite, new_status):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from datetime import datetime
|
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
import pendulum
|
||||||
|
|
||||||
from atst.database import db
|
from atst.database import db
|
||||||
from atst.models.clin import CLIN
|
from atst.models.clin import CLIN
|
||||||
@ -41,8 +41,7 @@ class TaskOrders(BaseDomainClass):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def sign(cls, task_order, signer_dod_id):
|
def sign(cls, task_order, signer_dod_id):
|
||||||
task_order.signer_dod_id = signer_dod_id
|
task_order.signer_dod_id = signer_dod_id
|
||||||
task_order.signed_at = datetime.now()
|
task_order.signed_at = pendulum.now(tz="utc")
|
||||||
|
|
||||||
db.session.add(task_order)
|
db.session.add(task_order)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from datetime import datetime
|
import pendulum
|
||||||
|
|
||||||
from atst.database import db
|
from atst.database import db
|
||||||
from atst.models import User
|
from atst.models import User
|
||||||
@ -111,7 +111,7 @@ class Users(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_last_login(cls, user):
|
def update_last_login(cls, user):
|
||||||
user.last_login = datetime.now()
|
user.last_login = pendulum.now(tz="utc")
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import re
|
import re
|
||||||
import datetime
|
|
||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from jinja2 import contextfilter
|
from jinja2 import contextfilter
|
||||||
@ -54,10 +53,6 @@ def formattedDate(value, formatter="%m/%d/%Y"):
|
|||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
|
|
||||||
def dateFromString(value, formatter="%m/%Y"):
|
|
||||||
return datetime.datetime.strptime(value, formatter)
|
|
||||||
|
|
||||||
|
|
||||||
def pageWindow(pagination, size=2):
|
def pageWindow(pagination, size=2):
|
||||||
page = pagination.page
|
page = pagination.page
|
||||||
num_pages = pagination.pages
|
num_pages = pagination.pages
|
||||||
@ -81,7 +76,6 @@ def register_filters(app):
|
|||||||
app.jinja_env.filters["dollars"] = dollars
|
app.jinja_env.filters["dollars"] = dollars
|
||||||
app.jinja_env.filters["usPhone"] = usPhone
|
app.jinja_env.filters["usPhone"] = usPhone
|
||||||
app.jinja_env.filters["formattedDate"] = formattedDate
|
app.jinja_env.filters["formattedDate"] = formattedDate
|
||||||
app.jinja_env.filters["dateFromString"] = dateFromString
|
|
||||||
app.jinja_env.filters["pageWindow"] = pageWindow
|
app.jinja_env.filters["pageWindow"] = pageWindow
|
||||||
app.jinja_env.filters["renderAuditEvent"] = renderAuditEvent
|
app.jinja_env.filters["renderAuditEvent"] = renderAuditEvent
|
||||||
app.jinja_env.filters["withExtraParams"] = with_extra_params
|
app.jinja_env.filters["withExtraParams"] = with_extra_params
|
||||||
|
@ -9,7 +9,7 @@ from sqlalchemy import (
|
|||||||
String,
|
String,
|
||||||
)
|
)
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from datetime import date
|
import pendulum
|
||||||
|
|
||||||
from atst.models.base import Base
|
from atst.models.base import Base
|
||||||
import atst.models.mixins as mixins
|
import atst.models.mixins as mixins
|
||||||
@ -75,5 +75,5 @@ class CLIN(Base, mixins.TimestampsMixin):
|
|||||||
@property
|
@property
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
return (
|
return (
|
||||||
self.start_date <= date.today() <= self.end_date
|
self.start_date <= pendulum.today() <= self.end_date
|
||||||
) and self.task_order.signed_at
|
) and self.task_order.signed_at
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import datetime
|
import pendulum
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import secrets
|
import secrets
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ class InvitesMixin(object):
|
|||||||
@property
|
@property
|
||||||
def is_expired(self):
|
def is_expired(self):
|
||||||
return (
|
return (
|
||||||
datetime.datetime.now(self.expiration_time.tzinfo) > self.expiration_time
|
pendulum.now(tz=self.expiration_time.tzinfo) > self.expiration_time
|
||||||
and not self.status == Status.ACCEPTED
|
and not self.status == Status.ACCEPTED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from sqlalchemy import Column, DateTime, ForeignKey, String
|
from sqlalchemy import Column, DateTime, ForeignKey, String
|
||||||
from sqlalchemy.ext.hybrid import hybrid_property
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
@ -141,14 +140,6 @@ class TaskOrder(Base, mixins.TimestampsMixin):
|
|||||||
def total_contract_amount(self):
|
def total_contract_amount(self):
|
||||||
return sum((clin.total_amount for clin in self.clins if clin.total_amount))
|
return sum((clin.total_amount for clin in self.clins if clin.total_amount))
|
||||||
|
|
||||||
@property
|
|
||||||
def invoiced_funds(self):
|
|
||||||
# TODO: implement this using reporting data from the CSP
|
|
||||||
if self.is_active:
|
|
||||||
return self.total_obligated_funds * Decimal(0.75)
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def display_status(self):
|
def display_status(self):
|
||||||
if self.status == Status.UNSIGNED:
|
if self.status == Status.UNSIGNED:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from datetime import datetime
|
import pendulum
|
||||||
|
|
||||||
from flask import redirect, render_template, url_for, request as http_request, g
|
from flask import redirect, render_template, url_for, request as http_request, g
|
||||||
|
|
||||||
from .blueprint import portfolios_bp
|
from .blueprint import portfolios_bp
|
||||||
@ -56,5 +55,5 @@ def reports(portfolio_id):
|
|||||||
),
|
),
|
||||||
current_obligated_funds=current_obligated_funds,
|
current_obligated_funds=current_obligated_funds,
|
||||||
expired_task_orders=Reports.expired_task_orders(portfolio),
|
expired_task_orders=Reports.expired_task_orders(portfolio),
|
||||||
retrieved=datetime.now(), # mocked datetime of reporting data retrival
|
retrieved=pendulum.now(), # mocked datetime of reporting data retrival
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import datetime as dt
|
import pendulum
|
||||||
from flask import Blueprint, render_template, g, request as http_request, redirect
|
from flask import Blueprint, render_template, g, request as http_request, redirect
|
||||||
from atst.forms.edit_user import EditUserForm
|
from atst.forms.edit_user import EditUserForm
|
||||||
from atst.domain.users import Users
|
from atst.domain.users import Users
|
||||||
@ -23,8 +23,8 @@ def user():
|
|||||||
next=next_,
|
next=next_,
|
||||||
form=form,
|
form=form,
|
||||||
user=user,
|
user=user,
|
||||||
mindate=(dt.datetime.now() - dt.timedelta(days=365)),
|
mindate=pendulum.now(tz="utc").subtract(days=365),
|
||||||
maxdate=dt.datetime.now(),
|
maxdate=pendulum.now(tz="utc"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -44,6 +44,6 @@ def update_user():
|
|||||||
form=form,
|
form=form,
|
||||||
user=user,
|
user=user,
|
||||||
next=next_url,
|
next=next_url,
|
||||||
mindate=(dt.datetime.now() - dt.timedelta(days=365)),
|
mindate=pendulum.now(tz="utc").subtract(days=365),
|
||||||
maxdate=dt.datetime.now(),
|
maxdate=pendulum.now(tz="utc"),
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Add root application dir to the python path
|
# Add root application dir to the python path
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import timedelta, date
|
import pendulum
|
||||||
import random
|
import random
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
from werkzeug.datastructures import FileStorage
|
from werkzeug.datastructures import FileStorage
|
||||||
@ -170,10 +170,9 @@ def add_members_to_portfolio(portfolio):
|
|||||||
|
|
||||||
|
|
||||||
def add_task_orders_to_portfolio(portfolio):
|
def add_task_orders_to_portfolio(portfolio):
|
||||||
today = date.today()
|
today = pendulum.today()
|
||||||
future = today + timedelta(days=100)
|
future = today.add(days=100)
|
||||||
yesterday = today - timedelta(days=1)
|
yesterday = today.subtract(days=1)
|
||||||
five_days = timedelta(days=5)
|
|
||||||
|
|
||||||
def build_pdf():
|
def build_pdf():
|
||||||
return {"filename": "sample_task_order.pdf", "object_name": str(uuid4())}
|
return {"filename": "sample_task_order.pdf", "object_name": str(uuid4())}
|
||||||
@ -192,13 +191,13 @@ def add_task_orders_to_portfolio(portfolio):
|
|||||||
|
|
||||||
clins = [
|
clins = [
|
||||||
CLINFactory.build(
|
CLINFactory.build(
|
||||||
task_order=unsigned_to, start_date=(today - five_days), end_date=today
|
task_order=unsigned_to, start_date=today.subtract(days=5), end_date=today
|
||||||
),
|
),
|
||||||
CLINFactory.build(
|
CLINFactory.build(
|
||||||
task_order=upcoming_to, start_date=(today + five_days), end_date=future
|
task_order=upcoming_to, start_date=today.add(days=5), end_date=future
|
||||||
),
|
),
|
||||||
CLINFactory.build(
|
CLINFactory.build(
|
||||||
task_order=expired_to, start_date=(today - five_days), end_date=yesterday
|
task_order=expired_to, start_date=today.subtract(days=5), end_date=yesterday
|
||||||
),
|
),
|
||||||
CLINFactory.build(
|
CLINFactory.build(
|
||||||
task_order=active_to,
|
task_order=active_to,
|
||||||
|
@ -20,14 +20,11 @@
|
|||||||
|
|
||||||
.col--grow {
|
.col--grow {
|
||||||
overflow: inherit;
|
overflow: inherit;
|
||||||
display: table;
|
align-self: center;
|
||||||
min-height: 10rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
@include h1;
|
@include h1;
|
||||||
display: table-cell;
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -60,6 +60,12 @@
|
|||||||
margin: ($gap * 2) 0;
|
margin: ($gap * 2) 0;
|
||||||
max-width: 75rem;
|
max-width: 75rem;
|
||||||
|
|
||||||
|
&-label-helper {
|
||||||
|
font-size: $small-font-size;
|
||||||
|
margin-left: $gap;
|
||||||
|
margin-right: $gap;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
padding: 0 0 ($gap / 2) 0;
|
padding: 0 0 ($gap / 2) 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<div class="usa-input__title">
|
<div class="usa-input__title">
|
||||||
{{ field.label | striptags }}
|
{{ field.label | striptags }}
|
||||||
{% if optional %}
|
{% if optional %}
|
||||||
<span class="usa-input-label-helper">(optional)</span>
|
<span class="usa-input-label-helper">{{ "common.optional" | translate }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if tooltip %}{{ Tooltip(tooltip) }}{% endif %}
|
{% if tooltip %}{{ Tooltip(tooltip) }}{% endif %}
|
||||||
{% if not field.description %}
|
{% if not field.description %}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<div class="usa-input__title{% if not field.description %}-inline{% endif %}">
|
<div class="usa-input__title{% if not field.description %}-inline{% endif %}">
|
||||||
{{ field.label | striptags}}
|
{{ field.label | striptags}}
|
||||||
{% if optional %}
|
{% if optional %}
|
||||||
<span class="usa-input-label-helper">(optional)</span>
|
<span class="usa-input-label-helper">{{ "common.optional" | translate }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if tooltip %}{{ Tooltip(tooltip) }}{% endif %}
|
{% if tooltip %}{{ Tooltip(tooltip) }}{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<div class="usa-input__title">
|
<div class="usa-input__title">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
{% if optional and showOptional %}
|
{% if optional and showOptional %}
|
||||||
<span class="usa-input-label-helper">(optional)</span>
|
<span class="usa-input-label-helper">{{ "common.optional" | translate }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if tooltip and not disabled %}
|
{% if tooltip and not disabled %}
|
||||||
{{ Tooltip(tooltip, tooltip_title) }}
|
{{ Tooltip(tooltip, tooltip_title) }}
|
||||||
|
@ -17,36 +17,37 @@
|
|||||||
<h1>{{ portfolio.name }}</h1>
|
<h1>{{ portfolio.name }}</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='row links'>
|
<div class="col">
|
||||||
{% if user_can(permissions.VIEW_PORTFOLIO_ADMIN) %}
|
<div class='row links'>
|
||||||
|
{% if user_can(permissions.VIEW_PORTFOLIO_ADMIN) %}
|
||||||
|
{{ Link(
|
||||||
|
icon='cog',
|
||||||
|
text='navigation.portfolio_navigation.breadcrumbs.admin' | translate,
|
||||||
|
url=url_for("portfolios.admin", portfolio_id=portfolio.id),
|
||||||
|
active=request.url_rule.endpoint == "portfolios.admin",
|
||||||
|
) }}
|
||||||
|
{% endif %}{% if user_can(permissions.VIEW_PORTFOLIO_FUNDING) %}
|
||||||
|
{{ Link(
|
||||||
|
icon='funding',
|
||||||
|
text='navigation.portfolio_navigation.breadcrumbs.funding' | translate,
|
||||||
|
url=url_for("task_orders.portfolio_funding", portfolio_id=portfolio.id),
|
||||||
|
active=request.url_rule.endpoint in ["task_orders.portfolio_funding", "task_orders.view_task_order", "task_orders.form_step_one_add_pdf", "task_orders.submit_form_step_one_add_pdf", "task_orders.form_step_two_add_number", "task_orders.submit_form_step_two_add_number", "task_orders.form_step_three_add_clins", "task_orders.submit_form_step_three_add_clins", "task_orders.form_step_four_review", "task_orders.form_step_five_confirm_signature"],
|
||||||
|
) }}
|
||||||
|
{% endif %}
|
||||||
{{ Link(
|
{{ Link(
|
||||||
icon='cog',
|
icon='applications',
|
||||||
text='navigation.portfolio_navigation.breadcrumbs.admin' | translate,
|
text='navigation.portfolio_navigation.breadcrumbs.applications' | translate,
|
||||||
url=url_for("portfolios.admin", portfolio_id=portfolio.id),
|
url=url_for("applications.portfolio_applications", portfolio_id=portfolio.id),
|
||||||
active=request.url_rule.endpoint == "portfolios.admin",
|
active=request.url_rule.endpoint in ["applications.portfolio_applications", "applications.settings"],
|
||||||
) }}
|
) }}
|
||||||
{% endif %}{% if user_can(permissions.VIEW_PORTFOLIO_FUNDING) %}
|
{% if user_can(permissions.VIEW_PORTFOLIO_REPORTS) %}
|
||||||
{{ Link(
|
{{ Link(
|
||||||
icon='funding',
|
icon='chart-pie',
|
||||||
text='navigation.portfolio_navigation.breadcrumbs.funding' | translate,
|
text='navigation.portfolio_navigation.breadcrumbs.reports' | translate,
|
||||||
url=url_for("task_orders.portfolio_funding", portfolio_id=portfolio.id),
|
url=url_for("portfolios.reports", portfolio_id=portfolio.id),
|
||||||
active=request.url_rule.endpoint in ["task_orders.portfolio_funding", "task_orders.view_task_order", "task_orders.form_step_one_add_pdf", "task_orders.submit_form_step_one_add_pdf", "task_orders.form_step_two_add_number", "task_orders.submit_form_step_two_add_number", "task_orders.form_step_three_add_clins", "task_orders.submit_form_step_three_add_clins", "task_orders.form_step_four_review", "task_orders.form_step_five_confirm_signature"],
|
active=request.url_rule.endpoint == "portfolios.reports",
|
||||||
) }}
|
) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ Link(
|
</div>
|
||||||
icon='applications',
|
|
||||||
text='navigation.portfolio_navigation.breadcrumbs.applications' | translate,
|
|
||||||
url=url_for("applications.portfolio_applications", portfolio_id=portfolio.id),
|
|
||||||
active=request.url_rule.endpoint in ["applications.portfolio_applications", "applications.settings"],
|
|
||||||
) }}
|
|
||||||
{% if user_can(permissions.VIEW_PORTFOLIO_REPORTS) %}
|
|
||||||
{{ Link(
|
|
||||||
icon='chart-pie',
|
|
||||||
text='navigation.portfolio_navigation.breadcrumbs.reports' | translate,
|
|
||||||
url=url_for("portfolios.reports", portfolio_id=portfolio.id),
|
|
||||||
active=request.url_rule.endpoint == "portfolios.reports",
|
|
||||||
) }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
{% set expended_funds = task_order.invoiced_funds %}
|
{% set expended_funds = task_order.invoiced_funds %}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<section class="row">
|
<section class="usa-grid">
|
||||||
<div class='col col--grow summary-item'>
|
<div class='usa-width-one-third summary-item'>
|
||||||
<h4 class="summary-item__header">
|
<h4 class="summary-item__header">
|
||||||
<span class="summary-item__header-text">{{ 'task_orders.summary.total' | translate }}</span>
|
<span class="summary-item__header-text">{{ 'task_orders.summary.total' | translate }}</span>
|
||||||
{{ Tooltip(("task_orders.review.tooltip.total_value" | translate), title="", classes="icon-tooltip--tight") }}
|
{{ Tooltip(("task_orders.review.tooltip.total_value" | translate), title="", classes="icon-tooltip--tight") }}
|
||||||
@ -18,7 +18,7 @@
|
|||||||
{{ contract_amount | dollars }}
|
{{ contract_amount | dollars }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class='col col--grow summary-item'>
|
<div class='usa-width-one-third summary-item'>
|
||||||
<h4 class="summary-item__header">
|
<h4 class="summary-item__header">
|
||||||
<span class="summary-item__header-text">{{ 'task_orders.summary.obligated' | translate }}</span>
|
<span class="summary-item__header-text">{{ 'task_orders.summary.obligated' | translate }}</span>
|
||||||
{{ Tooltip(("task_orders.review.tooltip.obligated_funds" | translate), title="", classes="icon-tooltip--tight") }}
|
{{ Tooltip(("task_orders.review.tooltip.obligated_funds" | translate), title="", classes="icon-tooltip--tight") }}
|
||||||
@ -27,15 +27,6 @@
|
|||||||
{{ obligated_funds | dollars }}
|
{{ obligated_funds | dollars }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class='col col--grow summary-item'>
|
|
||||||
<h4 class="summary-item__header">
|
|
||||||
<span class="summary-item__header-text">{{ 'task_orders.summary.expended' | translate }}</span>
|
|
||||||
{{ Tooltip(("task_orders.review.tooltip.expended_funds" | translate), title="", classes="icon-tooltip--tight") }}
|
|
||||||
</h4>
|
|
||||||
<p class="summary-item__value--large">
|
|
||||||
{{ expended_funds | dollars }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
<hr>
|
<hr>
|
||||||
<section>
|
<section>
|
||||||
|
@ -34,8 +34,8 @@
|
|||||||
<div class="accordion__content--list-item">
|
<div class="accordion__content--list-item">
|
||||||
<h4><a href="{{ url_for('task_orders.view_task_order', task_order_id=task_order.id) }}">{{ to_number }} {{ Icon("caret_right", classes="icon--tiny icon--primary" ) }}</a></h4>
|
<h4><a href="{{ url_for('task_orders.view_task_order', task_order_id=task_order.id) }}">{{ to_number }} {{ Icon("caret_right", classes="icon--tiny icon--primary" ) }}</a></h4>
|
||||||
{% if status != 'Expired' -%}
|
{% if status != 'Expired' -%}
|
||||||
<div class="row">
|
<div class="usa-grid">
|
||||||
<div class="col col--grow">
|
<div class="usa-width-one-fourth">
|
||||||
<h5>
|
<h5>
|
||||||
Current Period of Performance
|
Current Period of Performance
|
||||||
</h5>
|
</h5>
|
||||||
@ -45,18 +45,14 @@
|
|||||||
{{ task_order.end_date | formattedDate(formatter="%b %d, %Y") }}
|
{{ task_order.end_date | formattedDate(formatter="%b %d, %Y") }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col--grow">
|
<div class="usa-width-one-fourth">
|
||||||
<h5>Total Value</h5>
|
<h5>Total Value</h5>
|
||||||
<p>{{ task_order.total_contract_amount | dollars }}</p>
|
<p>{{ task_order.total_contract_amount | dollars }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col--grow">
|
<div class="usa-width-one-fourth">
|
||||||
<h5>Total Obligated</h5>
|
<h5>Total Obligated</h5>
|
||||||
<p>{{ task_order.total_obligated_funds | dollars }}</p>
|
<p>{{ task_order.total_obligated_funds | dollars }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col--grow">
|
|
||||||
<h5>Total Expended</h5>
|
|
||||||
<p>{{ task_order.invoiced_funds | dollars }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,7 +13,7 @@ import tests.factories as factories
|
|||||||
from tests.mocks import PDF_FILENAME, PDF_FILENAME2
|
from tests.mocks import PDF_FILENAME, PDF_FILENAME2
|
||||||
from tests.utils import FakeLogger, FakeNotificationSender
|
from tests.utils import FakeLogger, FakeNotificationSender
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
import pendulum
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
from cryptography import x509
|
from cryptography import x509
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
@ -175,7 +175,7 @@ def extended_financial_verification_data(pdf_upload):
|
|||||||
return {
|
return {
|
||||||
"funding_type": "RDTE",
|
"funding_type": "RDTE",
|
||||||
"funding_type_other": "other",
|
"funding_type_other": "other",
|
||||||
"expiration_date": "1/1/{}".format(datetime.date.today().year + 1),
|
"expiration_date": "1/1/{}".format(pendulum.today().year + 1),
|
||||||
"clin_0001": "50000",
|
"clin_0001": "50000",
|
||||||
"clin_0003": "13000",
|
"clin_0003": "13000",
|
||||||
"clin_1001": "30000",
|
"clin_1001": "30000",
|
||||||
@ -214,7 +214,6 @@ def make_x509():
|
|||||||
if signer_key is None:
|
if signer_key is None:
|
||||||
signer_key = private_key
|
signer_key = private_key
|
||||||
|
|
||||||
one_day = timedelta(1, 0, 0)
|
|
||||||
public_key = private_key.public_key()
|
public_key = private_key.public_key()
|
||||||
builder = x509.CertificateBuilder()
|
builder = x509.CertificateBuilder()
|
||||||
builder = builder.subject_name(
|
builder = builder.subject_name(
|
||||||
@ -227,8 +226,8 @@ def make_x509():
|
|||||||
builder = builder.add_extension(
|
builder = builder.add_extension(
|
||||||
x509.BasicConstraints(ca=True, path_length=None), critical=True
|
x509.BasicConstraints(ca=True, path_length=None), critical=True
|
||||||
)
|
)
|
||||||
builder = builder.not_valid_before(datetime.today() - (one_day * 2))
|
builder = builder.not_valid_before(pendulum.today().subtract(days=2))
|
||||||
builder = builder.not_valid_after(datetime.today() + (one_day * 30))
|
builder = builder.not_valid_after(pendulum.today().add(days=30))
|
||||||
builder = builder.serial_number(x509.random_serial_number())
|
builder = builder.serial_number(x509.random_serial_number())
|
||||||
builder = builder.public_key(public_key)
|
builder = builder.public_key(public_key)
|
||||||
certificate = builder.sign(
|
certificate = builder.sign(
|
||||||
@ -249,13 +248,12 @@ def make_crl():
|
|||||||
cn="ATAT",
|
cn="ATAT",
|
||||||
expired_serials=None,
|
expired_serials=None,
|
||||||
):
|
):
|
||||||
one_day = timedelta(1, 0, 0)
|
|
||||||
builder = x509.CertificateRevocationListBuilder()
|
builder = x509.CertificateRevocationListBuilder()
|
||||||
builder = builder.issuer_name(
|
builder = builder.issuer_name(
|
||||||
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, cn)])
|
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, cn)])
|
||||||
)
|
)
|
||||||
last_update = datetime.today() + (one_day * last_update_days)
|
last_update = pendulum.today().add(days=last_update_days)
|
||||||
next_update = datetime.today() + (one_day * next_update_days)
|
next_update = pendulum.today().add(days=next_update_days)
|
||||||
builder = builder.last_update(last_update)
|
builder = builder.last_update(last_update)
|
||||||
builder = builder.next_update(next_update)
|
builder = builder.next_update(next_update)
|
||||||
if expired_serials:
|
if expired_serials:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timedelta
|
import pendulum
|
||||||
import pytest
|
import pytest
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -200,8 +200,8 @@ def test_update_does_not_duplicate_names_within_portfolio():
|
|||||||
|
|
||||||
|
|
||||||
def test_get_applications_pending_creation():
|
def test_get_applications_pending_creation():
|
||||||
now = datetime.now()
|
now = pendulum.now(tz="utc")
|
||||||
later = now + timedelta(minutes=30)
|
later = now.add(minutes=30)
|
||||||
|
|
||||||
portfolio1 = PortfolioFactory.create(state="COMPLETED")
|
portfolio1 = PortfolioFactory.create(state="COMPLETED")
|
||||||
app_ready = ApplicationFactory.create(portfolio=portfolio1)
|
app_ready = ApplicationFactory.create(portfolio=portfolio1)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import datetime
|
|
||||||
import pytest
|
import pytest
|
||||||
import re
|
import re
|
||||||
|
import pendulum
|
||||||
|
|
||||||
from atst.domain.audit_log import AuditLog
|
from atst.domain.audit_log import AuditLog
|
||||||
from atst.domain.invitations import (
|
from atst.domain.invitations import (
|
||||||
@ -53,7 +53,7 @@ def test_accept_expired_invitation():
|
|||||||
portfolio = PortfolioFactory.create()
|
portfolio = PortfolioFactory.create()
|
||||||
role = PortfolioRoleFactory.create(portfolio=portfolio)
|
role = PortfolioRoleFactory.create(portfolio=portfolio)
|
||||||
increment = PortfolioInvitations.EXPIRATION_LIMIT_MINUTES + 1
|
increment = PortfolioInvitations.EXPIRATION_LIMIT_MINUTES + 1
|
||||||
expiration_time = datetime.datetime.now() - datetime.timedelta(minutes=increment)
|
expiration_time = pendulum.now(tz="utc").subtract(minutes=increment)
|
||||||
invite = PortfolioInvitationFactory.create(
|
invite = PortfolioInvitationFactory.create(
|
||||||
expiration_time=expiration_time,
|
expiration_time=expiration_time,
|
||||||
status=InvitationStatus.PENDING,
|
status=InvitationStatus.PENDING,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from datetime import date, datetime, timedelta
|
import pendulum
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from atst.domain.exceptions import AlreadyExistsError
|
from atst.domain.exceptions import AlreadyExistsError
|
||||||
@ -15,16 +15,16 @@ def test_create_adds_clins():
|
|||||||
{
|
{
|
||||||
"jedi_clin_type": "JEDI_CLIN_1",
|
"jedi_clin_type": "JEDI_CLIN_1",
|
||||||
"number": "12312",
|
"number": "12312",
|
||||||
"start_date": date(2020, 1, 1),
|
"start_date": pendulum.date(2020, 1, 1),
|
||||||
"end_date": date(2021, 1, 1),
|
"end_date": pendulum.date(2021, 1, 1),
|
||||||
"obligated_amount": Decimal("5000"),
|
"obligated_amount": Decimal("5000"),
|
||||||
"total_amount": Decimal("10000"),
|
"total_amount": Decimal("10000"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"jedi_clin_type": "JEDI_CLIN_1",
|
"jedi_clin_type": "JEDI_CLIN_1",
|
||||||
"number": "12312",
|
"number": "12312",
|
||||||
"start_date": date(2020, 1, 1),
|
"start_date": pendulum.date(2020, 1, 1),
|
||||||
"end_date": date(2021, 1, 1),
|
"end_date": pendulum.date(2021, 1, 1),
|
||||||
"obligated_amount": Decimal("5000"),
|
"obligated_amount": Decimal("5000"),
|
||||||
"total_amount": Decimal("10000"),
|
"total_amount": Decimal("10000"),
|
||||||
},
|
},
|
||||||
@ -45,16 +45,16 @@ def test_update_adds_clins():
|
|||||||
{
|
{
|
||||||
"jedi_clin_type": "JEDI_CLIN_1",
|
"jedi_clin_type": "JEDI_CLIN_1",
|
||||||
"number": "12312",
|
"number": "12312",
|
||||||
"start_date": date(2020, 1, 1),
|
"start_date": pendulum.date(2020, 1, 1),
|
||||||
"end_date": date(2021, 1, 1),
|
"end_date": pendulum.date(2021, 1, 1),
|
||||||
"obligated_amount": Decimal("5000"),
|
"obligated_amount": Decimal("5000"),
|
||||||
"total_amount": Decimal("10000"),
|
"total_amount": Decimal("10000"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"jedi_clin_type": "JEDI_CLIN_1",
|
"jedi_clin_type": "JEDI_CLIN_1",
|
||||||
"number": "12312",
|
"number": "12312",
|
||||||
"start_date": date(2020, 1, 1),
|
"start_date": pendulum.date(2020, 1, 1),
|
||||||
"end_date": date(2021, 1, 1),
|
"end_date": pendulum.date(2021, 1, 1),
|
||||||
"obligated_amount": Decimal("5000"),
|
"obligated_amount": Decimal("5000"),
|
||||||
"total_amount": Decimal("10000"),
|
"total_amount": Decimal("10000"),
|
||||||
},
|
},
|
||||||
@ -77,16 +77,16 @@ def test_update_does_not_duplicate_clins():
|
|||||||
{
|
{
|
||||||
"jedi_clin_type": "JEDI_CLIN_1",
|
"jedi_clin_type": "JEDI_CLIN_1",
|
||||||
"number": "123",
|
"number": "123",
|
||||||
"start_date": date(2020, 1, 1),
|
"start_date": pendulum.date(2020, 1, 1),
|
||||||
"end_date": date(2021, 1, 1),
|
"end_date": pendulum.date(2021, 1, 1),
|
||||||
"obligated_amount": Decimal("5000"),
|
"obligated_amount": Decimal("5000"),
|
||||||
"total_amount": Decimal("10000"),
|
"total_amount": Decimal("10000"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"jedi_clin_type": "JEDI_CLIN_1",
|
"jedi_clin_type": "JEDI_CLIN_1",
|
||||||
"number": "111",
|
"number": "111",
|
||||||
"start_date": date(2020, 1, 1),
|
"start_date": pendulum.date(2020, 1, 1),
|
||||||
"end_date": date(2021, 1, 1),
|
"end_date": pendulum.date(2021, 1, 1),
|
||||||
"obligated_amount": Decimal("5000"),
|
"obligated_amount": Decimal("5000"),
|
||||||
"total_amount": Decimal("10000"),
|
"total_amount": Decimal("10000"),
|
||||||
},
|
},
|
||||||
@ -114,9 +114,9 @@ def test_delete_task_order_with_clins(session):
|
|||||||
|
|
||||||
|
|
||||||
def test_task_order_sort_by_status():
|
def test_task_order_sort_by_status():
|
||||||
today = date.today()
|
today = pendulum.today()
|
||||||
yesterday = today - timedelta(days=1)
|
yesterday = today.subtract(days=1)
|
||||||
future = today + timedelta(days=100)
|
future = today.add(days=100)
|
||||||
|
|
||||||
initial_to_list = [
|
initial_to_list = [
|
||||||
# Draft
|
# Draft
|
||||||
@ -184,12 +184,12 @@ def test_allows_alphanumeric_number():
|
|||||||
def test_get_for_send_task_order_files():
|
def test_get_for_send_task_order_files():
|
||||||
new_to = TaskOrderFactory.create(create_clins=[{}])
|
new_to = TaskOrderFactory.create(create_clins=[{}])
|
||||||
updated_to = TaskOrderFactory.create(
|
updated_to = TaskOrderFactory.create(
|
||||||
create_clins=[{"last_sent_at": datetime(2020, 2, 1)}],
|
create_clins=[{"last_sent_at": pendulum.datetime(2020, 2, 1)}],
|
||||||
pdf_last_sent_at=datetime(2020, 1, 1),
|
pdf_last_sent_at=pendulum.datetime(2020, 1, 1),
|
||||||
)
|
)
|
||||||
sent_to = TaskOrderFactory.create(
|
sent_to = TaskOrderFactory.create(
|
||||||
create_clins=[{"last_sent_at": datetime(2020, 1, 1)}],
|
create_clins=[{"last_sent_at": pendulum.datetime(2020, 1, 1)}],
|
||||||
pdf_last_sent_at=datetime(2020, 1, 1),
|
pdf_last_sent_at=pendulum.datetime(2020, 1, 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
updated_and_new_task_orders = TaskOrders.get_for_send_task_order_files()
|
updated_and_new_task_orders = TaskOrders.get_for_send_task_order_files()
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from atst.domain.users import Users
|
from atst.domain.users import Users
|
||||||
|
@ -3,7 +3,7 @@ import random
|
|||||||
import string
|
import string
|
||||||
import factory
|
import factory
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
import datetime
|
import pendulum
|
||||||
|
|
||||||
from atst.forms import data
|
from atst.forms import data
|
||||||
from atst.models import *
|
from atst.models import *
|
||||||
@ -56,8 +56,8 @@ def _random_date(year_min, year_max, operation):
|
|||||||
else:
|
else:
|
||||||
inc = random.randrange(year_min, year_max)
|
inc = random.randrange(year_min, year_max)
|
||||||
|
|
||||||
return datetime.date(
|
return pendulum.date(
|
||||||
operation(datetime.date.today().year, inc),
|
operation(pendulum.today().year, inc),
|
||||||
random.randrange(1, 12),
|
random.randrange(1, 12),
|
||||||
random.randrange(1, 28),
|
random.randrange(1, 28),
|
||||||
)
|
)
|
||||||
@ -99,8 +99,7 @@ class UserFactory(Base):
|
|||||||
citizenship = "United States"
|
citizenship = "United States"
|
||||||
designation = "military"
|
designation = "military"
|
||||||
date_latest_training = factory.LazyFunction(
|
date_latest_training = factory.LazyFunction(
|
||||||
lambda: datetime.date.today()
|
lambda: pendulum.today().add(days=-(random.randrange(1, 365)))
|
||||||
+ datetime.timedelta(days=-(random.randrange(1, 365)))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -341,7 +340,7 @@ class CLINFactory(Base):
|
|||||||
|
|
||||||
task_order = factory.SubFactory(TaskOrderFactory)
|
task_order = factory.SubFactory(TaskOrderFactory)
|
||||||
number = factory.LazyFunction(random_clin_number)
|
number = factory.LazyFunction(random_clin_number)
|
||||||
start_date = datetime.date.today()
|
start_date = pendulum.today()
|
||||||
end_date = factory.LazyFunction(random_future_date)
|
end_date = factory.LazyFunction(random_future_date)
|
||||||
total_amount = factory.LazyFunction(lambda *args: random.randint(50000, 999999))
|
total_amount = factory.LazyFunction(lambda *args: random.randint(50000, 999999))
|
||||||
obligated_amount = factory.LazyFunction(lambda *args: random.randint(100, 50000))
|
obligated_amount = factory.LazyFunction(lambda *args: random.randint(100, 50000))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import datetime
|
import pendulum
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
|
|
||||||
@ -17,8 +17,8 @@ def test_clin_form_jedi_clin_type():
|
|||||||
|
|
||||||
|
|
||||||
def test_clin_form_start_date_before_end_date():
|
def test_clin_form_start_date_before_end_date():
|
||||||
invalid_start = datetime.date(2020, 12, 12)
|
invalid_start = pendulum.date(2020, 12, 12)
|
||||||
invalid_end = datetime.date(2020, 1, 1)
|
invalid_end = pendulum.date(2020, 1, 1)
|
||||||
invalid_clin = factories.CLINFactory.create(
|
invalid_clin = factories.CLINFactory.create(
|
||||||
start_date=invalid_start, end_date=invalid_end
|
start_date=invalid_start, end_date=invalid_end
|
||||||
)
|
)
|
||||||
@ -28,8 +28,8 @@ def test_clin_form_start_date_before_end_date():
|
|||||||
translate("forms.task_order.pop_errors.date_order")
|
translate("forms.task_order.pop_errors.date_order")
|
||||||
in clin_form.start_date.errors
|
in clin_form.start_date.errors
|
||||||
)
|
)
|
||||||
valid_start = datetime.date(2020, 1, 1)
|
valid_start = pendulum.date(2020, 1, 1)
|
||||||
valid_end = datetime.date(2020, 12, 12)
|
valid_end = pendulum.date(2020, 12, 12)
|
||||||
valid_clin = factories.CLINFactory.create(
|
valid_clin = factories.CLINFactory.create(
|
||||||
start_date=valid_start, end_date=valid_end
|
start_date=valid_start, end_date=valid_end
|
||||||
)
|
)
|
||||||
@ -81,8 +81,8 @@ def test_clin_form_obligated_greater_than_total():
|
|||||||
invalid_clin = factories.CLINFactory.create(
|
invalid_clin = factories.CLINFactory.create(
|
||||||
total_amount=0,
|
total_amount=0,
|
||||||
obligated_amount=1,
|
obligated_amount=1,
|
||||||
start_date=datetime.date(2019, 9, 15),
|
start_date=pendulum.date(2019, 9, 15),
|
||||||
end_date=datetime.date(2020, 9, 14),
|
end_date=pendulum.date(2020, 9, 14),
|
||||||
)
|
)
|
||||||
invalid_clin_form = CLINForm(obj=invalid_clin)
|
invalid_clin_form = CLINForm(obj=invalid_clin)
|
||||||
assert not invalid_clin_form.validate()
|
assert not invalid_clin_form.validate()
|
||||||
@ -95,8 +95,8 @@ def test_clin_form_dollar_amounts_out_of_range():
|
|||||||
invalid_clin = factories.CLINFactory.create(
|
invalid_clin = factories.CLINFactory.create(
|
||||||
total_amount=-1,
|
total_amount=-1,
|
||||||
obligated_amount=1000000001,
|
obligated_amount=1000000001,
|
||||||
start_date=datetime.date(2019, 9, 15),
|
start_date=pendulum.date(2019, 9, 15),
|
||||||
end_date=datetime.date(2020, 9, 14),
|
end_date=pendulum.date(2020, 9, 14),
|
||||||
)
|
)
|
||||||
invalid_clin_form = CLINForm(obj=invalid_clin)
|
invalid_clin_form = CLINForm(obj=invalid_clin)
|
||||||
assert not invalid_clin_form.validate()
|
assert not invalid_clin_form.validate()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import pendulum
|
||||||
|
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
from atst.domain.environment_roles import EnvironmentRoles
|
from atst.domain.environment_roles import EnvironmentRoles
|
||||||
@ -61,7 +62,7 @@ def test_environment_roles():
|
|||||||
|
|
||||||
|
|
||||||
def test_display_status():
|
def test_display_status():
|
||||||
yesterday = datetime.date.today() - datetime.timedelta(days=1)
|
yesterday = pendulum.today().subtract(days=1)
|
||||||
expired_invite = ApplicationInvitationFactory.create(expiration_time=yesterday)
|
expired_invite = ApplicationInvitationFactory.create(expiration_time=yesterday)
|
||||||
assert expired_invite.role.display_status == "invite_expired"
|
assert expired_invite.role.display_status == "invite_expired"
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ from tests.factories import (
|
|||||||
random_future_date,
|
random_future_date,
|
||||||
random_past_date,
|
random_past_date,
|
||||||
)
|
)
|
||||||
import datetime
|
|
||||||
import pendulum
|
import pendulum
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import pytest
|
import pytest
|
||||||
@ -83,7 +82,7 @@ def test_funding_duration(session):
|
|||||||
portfolio=portfolio,
|
portfolio=portfolio,
|
||||||
signed_at=random_past_date(),
|
signed_at=random_past_date(),
|
||||||
create_clins=[
|
create_clins=[
|
||||||
{"start_date": datetime.datetime.now(), "end_date": funding_end_date,}
|
{"start_date": pendulum.now(tz="utc"), "end_date": funding_end_date,}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ def test_days_remaining(session):
|
|||||||
|
|
||||||
assert (
|
assert (
|
||||||
portfolio.days_to_funding_expiration
|
portfolio.days_to_funding_expiration
|
||||||
== (funding_end_date - datetime.date.today()).days
|
== (funding_end_date - pendulum.today()).days
|
||||||
)
|
)
|
||||||
|
|
||||||
# empty portfolio
|
# empty portfolio
|
||||||
@ -121,8 +120,8 @@ def test_active_task_orders(session):
|
|||||||
signed_at=random_past_date(),
|
signed_at=random_past_date(),
|
||||||
create_clins=[
|
create_clins=[
|
||||||
{
|
{
|
||||||
"start_date": datetime.date(2019, 1, 1),
|
"start_date": pendulum.date(2019, 1, 1),
|
||||||
"end_date": datetime.date(2019, 10, 31),
|
"end_date": pendulum.date(2019, 10, 31),
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import datetime
|
import pendulum
|
||||||
|
|
||||||
from atst.models import InvitationStatus, PortfolioRoleStatus
|
from atst.models import InvitationStatus, PortfolioRoleStatus
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ def test_expired_invite_is_not_revokable():
|
|||||||
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
|
portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING
|
||||||
)
|
)
|
||||||
invite = PortfolioInvitationFactory.create(
|
invite = PortfolioInvitationFactory.create(
|
||||||
expiration_time=datetime.datetime.now() - datetime.timedelta(minutes=60),
|
expiration_time=pendulum.now(tz="utc").subtract(minutes=60),
|
||||||
role=portfolio_role,
|
role=portfolio_role,
|
||||||
)
|
)
|
||||||
assert not invite.is_revokable
|
assert not invite.is_revokable
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import datetime
|
import pendulum
|
||||||
|
|
||||||
from atst.domain.environments import Environments
|
from atst.domain.environments import Environments
|
||||||
from atst.domain.portfolios import Portfolios
|
from atst.domain.portfolios import Portfolios
|
||||||
@ -204,7 +204,7 @@ def test_status_when_invitation_is_expired():
|
|||||||
PortfolioInvitationFactory.create(
|
PortfolioInvitationFactory.create(
|
||||||
role=portfolio_role,
|
role=portfolio_role,
|
||||||
status=InvitationStatus.PENDING,
|
status=InvitationStatus.PENDING,
|
||||||
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
|
expiration_time=pendulum.now(tz="utc").subtract(seconds=1),
|
||||||
)
|
)
|
||||||
assert portfolio_role.display_status == "invite_expired"
|
assert portfolio_role.display_status == "invite_expired"
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from werkzeug.datastructures import FileStorage
|
from werkzeug.datastructures import FileStorage
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import date
|
import pendulum
|
||||||
from unittest.mock import patch, PropertyMock
|
from unittest.mock import patch, PropertyMock
|
||||||
import pendulum
|
import pendulum
|
||||||
|
|
||||||
@ -13,11 +13,11 @@ from tests.mocks import PDF_FILENAME
|
|||||||
|
|
||||||
|
|
||||||
def test_period_of_performance_is_first_to_last_clin():
|
def test_period_of_performance_is_first_to_last_clin():
|
||||||
start_date = date(2019, 6, 6)
|
start_date = pendulum.date(2019, 6, 6)
|
||||||
end_date = date(2020, 6, 6)
|
end_date = pendulum.date(2020, 6, 6)
|
||||||
|
|
||||||
intermediate_start_date = date(2019, 7, 1)
|
intermediate_start_date = pendulum.date(2019, 7, 1)
|
||||||
intermediate_end_date = date(2020, 3, 1)
|
intermediate_end_date = pendulum.date(2020, 3, 1)
|
||||||
|
|
||||||
task_order = TaskOrderFactory.create(
|
task_order = TaskOrderFactory.create(
|
||||||
clins=[
|
clins=[
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from sqlalchemy.exc import InternalError
|
from sqlalchemy.exc import InternalError
|
||||||
from datetime import datetime
|
import pendulum
|
||||||
|
|
||||||
from atst.database import db
|
from atst.database import db
|
||||||
from atst.domain.users import Users
|
from atst.domain.users import Users
|
||||||
@ -44,7 +44,7 @@ def test_deleted_application_roles_are_ignored(session):
|
|||||||
|
|
||||||
def test_does_not_log_user_update_when_updating_last_login(mock_logger):
|
def test_does_not_log_user_update_when_updating_last_login(mock_logger):
|
||||||
user = UserFactory.create()
|
user = UserFactory.create()
|
||||||
user.last_login = datetime.now()
|
user.last_login = pendulum.now(tz="utc")
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
assert "Audit Event update" not in mock_logger.messages
|
assert "Audit Event update" not in mock_logger.messages
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import datetime
|
import pendulum
|
||||||
import uuid
|
import uuid
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ def test_filter_environment_roles():
|
|||||||
user = UserFactory.create()
|
user = UserFactory.create()
|
||||||
# need to set the time created to yesterday, otherwise the original invite and resent
|
# need to set the time created to yesterday, otherwise the original invite and resent
|
||||||
# invite have the same time_created and then we can't rely on time to order the invites
|
# invite have the same time_created and then we can't rely on time to order the invites
|
||||||
yesterday = datetime.date.today() - datetime.timedelta(days=1)
|
yesterday = pendulum.today().subtract(days=1)
|
||||||
invite = ApplicationInvitationFactory.create(
|
invite = ApplicationInvitationFactory.create(
|
||||||
user=user, time_created=yesterday, email="original@example.com"
|
user=user, time_created=yesterday, email="original@example.com"
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import datetime
|
import pendulum
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
@ -123,7 +123,7 @@ def test_user_accepts_expired_invite(client, user_session):
|
|||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
role=ws_role,
|
role=ws_role,
|
||||||
status=InvitationStatus.REJECTED_EXPIRED,
|
status=InvitationStatus.REJECTED_EXPIRED,
|
||||||
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
|
expiration_time=pendulum.now(tz="utc").subtract(seconds=1),
|
||||||
)
|
)
|
||||||
user_session(user)
|
user_session(user)
|
||||||
response = client.get(
|
response = client.get(
|
||||||
@ -143,7 +143,7 @@ def test_revoke_invitation(client, user_session):
|
|||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
role=ws_role,
|
role=ws_role,
|
||||||
status=InvitationStatus.REJECTED_EXPIRED,
|
status=InvitationStatus.REJECTED_EXPIRED,
|
||||||
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
|
expiration_time=pendulum.now(tz="utc").subtract(seconds=1),
|
||||||
)
|
)
|
||||||
user_session(portfolio.owner)
|
user_session(portfolio.owner)
|
||||||
response = client.post(
|
response = client.post(
|
||||||
@ -169,7 +169,7 @@ def test_user_can_only_revoke_invites_in_their_portfolio(client, user_session):
|
|||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
role=portfolio_role,
|
role=portfolio_role,
|
||||||
status=InvitationStatus.REJECTED_EXPIRED,
|
status=InvitationStatus.REJECTED_EXPIRED,
|
||||||
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
|
expiration_time=pendulum.now(tz="utc").subtract(seconds=1),
|
||||||
)
|
)
|
||||||
user_session(portfolio.owner)
|
user_session(portfolio.owner)
|
||||||
response = client.post(
|
response = client.post(
|
||||||
@ -199,7 +199,7 @@ def test_user_can_only_resend_invites_in_their_portfolio(
|
|||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
role=portfolio_role,
|
role=portfolio_role,
|
||||||
status=InvitationStatus.REJECTED_EXPIRED,
|
status=InvitationStatus.REJECTED_EXPIRED,
|
||||||
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
|
expiration_time=pendulum.now(tz="utc").subtract(seconds=1),
|
||||||
)
|
)
|
||||||
user_session(portfolio.owner)
|
user_session(portfolio.owner)
|
||||||
response = client.post(
|
response = client.post(
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
from datetime import date
|
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import timedelta, date
|
|
||||||
|
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
from atst.domain.task_orders import TaskOrders
|
from atst.domain.task_orders import TaskOrders
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from flask import url_for, get_flashed_messages
|
from flask import url_for, get_flashed_messages
|
||||||
from datetime import timedelta, date
|
import pendulum
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from atst.domain.task_orders import TaskOrders
|
from atst.domain.task_orders import TaskOrders
|
||||||
@ -339,7 +339,7 @@ def test_task_orders_submit_task_order(client, user_session, task_order):
|
|||||||
)
|
)
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
|
|
||||||
active_start_date = date.today() - timedelta(days=1)
|
active_start_date = pendulum.today().subtract(days=1)
|
||||||
active_task_order = TaskOrderFactory(portfolio=task_order.portfolio)
|
active_task_order = TaskOrderFactory(portfolio=task_order.portfolio)
|
||||||
CLINFactory(task_order=active_task_order, start_date=active_start_date)
|
CLINFactory(task_order=active_task_order, start_date=active_start_date)
|
||||||
assert active_task_order.status == TaskOrderStatus.UNSIGNED
|
assert active_task_order.status == TaskOrderStatus.UNSIGNED
|
||||||
@ -348,7 +348,7 @@ def test_task_orders_submit_task_order(client, user_session, task_order):
|
|||||||
)
|
)
|
||||||
assert active_task_order.status == TaskOrderStatus.ACTIVE
|
assert active_task_order.status == TaskOrderStatus.ACTIVE
|
||||||
|
|
||||||
upcoming_start_date = date.today() + timedelta(days=1)
|
upcoming_start_date = pendulum.today().add(days=1)
|
||||||
upcoming_task_order = TaskOrderFactory(portfolio=task_order.portfolio)
|
upcoming_task_order = TaskOrderFactory(portfolio=task_order.portfolio)
|
||||||
CLINFactory(task_order=upcoming_task_order, start_date=upcoming_start_date)
|
CLINFactory(task_order=upcoming_task_order, start_date=upcoming_start_date)
|
||||||
assert upcoming_task_order.status == TaskOrderStatus.UNSIGNED
|
assert upcoming_task_order.status == TaskOrderStatus.UNSIGNED
|
||||||
|
@ -3,11 +3,11 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import pendulum
|
||||||
from flask import session, url_for
|
from flask import session, url_for
|
||||||
from cryptography.hazmat.primitives.serialization import Encoding
|
from cryptography.hazmat.primitives.serialization import Encoding
|
||||||
|
|
||||||
from atst.domain.users import Users
|
from atst.domain.users import Users
|
||||||
from atst.domain.permission_sets import PermissionSets
|
|
||||||
from atst.domain.exceptions import NotFoundError
|
from atst.domain.exceptions import NotFoundError
|
||||||
from atst.domain.authnid.crl import CRLInvalidException
|
from atst.domain.authnid.crl import CRLInvalidException
|
||||||
from atst.domain.auth import UNPROTECTED_ROUTES
|
from atst.domain.auth import UNPROTECTED_ROUTES
|
||||||
@ -262,7 +262,7 @@ def test_error_on_invalid_crl(client, monkeypatch):
|
|||||||
|
|
||||||
|
|
||||||
def test_last_login_set_when_user_logs_in(client, monkeypatch):
|
def test_last_login_set_when_user_logs_in(client, monkeypatch):
|
||||||
last_login = datetime.now()
|
last_login = pendulum.now(tz="utc")
|
||||||
user = UserFactory.create(last_login=last_login)
|
user = UserFactory.create(last_login=last_login)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"atst.domain.authnid.AuthenticationContext.authenticate", lambda *args: True
|
"atst.domain.authnid.AuthenticationContext.authenticate", lambda *args: True
|
||||||
@ -270,7 +270,7 @@ def test_last_login_set_when_user_logs_in(client, monkeypatch):
|
|||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"atst.domain.authnid.AuthenticationContext.get_user", lambda *args: user
|
"atst.domain.authnid.AuthenticationContext.get_user", lambda *args: user
|
||||||
)
|
)
|
||||||
response = _login(client)
|
_login(client)
|
||||||
assert session["last_login"]
|
assert session["last_login"]
|
||||||
assert user.last_login > session["last_login"]
|
assert user.last_login > session["last_login"]
|
||||||
assert isinstance(session["last_login"], datetime)
|
assert isinstance(session["last_login"], datetime)
|
||||||
|
@ -51,6 +51,7 @@ common:
|
|||||||
lorem: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
lorem: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||||
name: Name
|
name: Name
|
||||||
next: Next
|
next: Next
|
||||||
|
optional: Optional
|
||||||
previous: Previous
|
previous: Previous
|
||||||
save: Save
|
save: Save
|
||||||
save_changes: Save Changes
|
save_changes: Save Changes
|
||||||
@ -506,12 +507,12 @@ portfolios:
|
|||||||
archive_button: Delete member
|
archive_button: Delete member
|
||||||
reports:
|
reports:
|
||||||
days_remaining:
|
days_remaining:
|
||||||
header: Days Remaining
|
header: Days remaining
|
||||||
toolip: Days remaining are the days of funding remaining in the portfolio.
|
toolip: Days remaining are the days of funding remaining in the portfolio.
|
||||||
duration:
|
duration:
|
||||||
header: Funding Duration
|
header: Funding duration
|
||||||
tooltip: Funding duration is the period of time that there is a valid task order funding the portfolio.
|
tooltip: Funding duration is the period of time that there is a valid task order funding the portfolio.
|
||||||
estimate_warning: Reports displayed in JEDI are estimates and not a system of record.
|
estimate_warning: Reports displayed in JEDI are estimates and not a system of record. To manage your costs, go to Azure by selecting the Login to Azure button above.
|
||||||
total_value:
|
total_value:
|
||||||
header: Total Portfolio Value
|
header: Total Portfolio Value
|
||||||
tooltip: Total portfolio value is all obligated funds for current and upcoming task orders in this portfolio.
|
tooltip: Total portfolio value is all obligated funds for current and upcoming task orders in this portfolio.
|
||||||
@ -533,7 +534,6 @@ task_orders:
|
|||||||
tooltip:
|
tooltip:
|
||||||
obligated_funds: Funds committed to fund your portfolio. This may represent 100% of your total Task Order value, or a portion of it.
|
obligated_funds: Funds committed to fund your portfolio. This may represent 100% of your total Task Order value, or a portion of it.
|
||||||
total_value: All obligated and projected funds for the Task Order’s Base and Option CLINs.
|
total_value: All obligated and projected funds for the Task Order’s Base and Option CLINs.
|
||||||
expended_funds: All funds spent from the Task Order so far.
|
|
||||||
form:
|
form:
|
||||||
add_clin: Add Another CLIN
|
add_clin: Add Another CLIN
|
||||||
add_to_header: Enter the Task Order number
|
add_to_header: Enter the Task Order number
|
||||||
@ -593,7 +593,6 @@ task_orders:
|
|||||||
summary:
|
summary:
|
||||||
obligated: Total Obligated
|
obligated: Total Obligated
|
||||||
total: Total Value
|
total: Total Value
|
||||||
expended: Total Expended
|
|
||||||
JEDICLINType:
|
JEDICLINType:
|
||||||
JEDI_CLIN_1: "IDIQ CLIN 0001 Unclassified IaaS/PaaS"
|
JEDI_CLIN_1: "IDIQ CLIN 0001 Unclassified IaaS/PaaS"
|
||||||
JEDI_CLIN_2: "IDIQ CLIN 0002 Classified IaaS/PaaS"
|
JEDI_CLIN_2: "IDIQ CLIN 0002 Classified IaaS/PaaS"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user