diff --git a/atst/app.py b/atst/app.py index 1499671c..d510ff82 100644 --- a/atst/app.py +++ b/atst/app.py @@ -1,7 +1,7 @@ import os import re 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_session import Session import redis @@ -187,11 +187,11 @@ def map_config(config): "CELERY_RESULT_EXPIRES": 0, "CELERY_RESULT_EXTENDED": True, "OFFICE_365_DOMAIN": "onmicrosoft.com", - "CONTRACT_START_DATE": datetime.strptime( - config.get("default", "CONTRACT_START_DATE"), "%Y-%m-%d" + "CONTRACT_START_DATE": pendulum.from_format( + config.get("default", "CONTRACT_START_DATE"), "YYYY-MM-DD" ).date(), - "CONTRACT_END_DATE": datetime.strptime( - config.get("default", "CONTRACT_END_DATE"), "%Y-%m-%d" + "CONTRACT_END_DATE": pendulum.from_format( + config.get("default", "CONTRACT_END_DATE"), "YYYY-MM-DD" ).date(), "SESSION_COOKIE_SECURE": config.getboolean("default", "SESSION_COOKIE_SECURE"), } diff --git a/atst/domain/authnid/crl/__init__.py b/atst/domain/authnid/crl/__init__.py index 037a8bb4..9a8f6638 100644 --- a/atst/domain/authnid/crl/__init__.py +++ b/atst/domain/authnid/crl/__init__.py @@ -4,7 +4,6 @@ import hashlib import logging from OpenSSL import crypto, SSL -from datetime import datetime from flask import current_app as app from .util import load_crl_locations_cache, serialize_crl_locations_cache, CRL_LIST diff --git a/atst/domain/csp/files.py b/atst/domain/csp/files.py index 0f3e05a0..f2f9383c 100644 --- a/atst/domain/csp/files.py +++ b/atst/domain/csp/files.py @@ -1,5 +1,5 @@ -from datetime import datetime, timedelta from uuid import uuid4 +import pendulum class FileService: @@ -39,7 +39,7 @@ class AzureFileService(FileService): self.account_name = config["AZURE_ACCOUNT_NAME"] self.storage_key = config["AZURE_STORAGE_KEY"] 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.blob import BlobSasPermissions @@ -68,7 +68,7 @@ class AzureFileService(FileService): self.container_name, object_name, permission=self.BlobSasPermissions(create=True), - expiry=datetime.utcnow() + self.timeout, + expiry=pendulum.now(tz="utc").add(self.timeout), protocol="https", ) return ({"token": sas_token}, object_name) @@ -81,7 +81,7 @@ class AzureFileService(FileService): container_name=self.container_name, blob_name=object_name, permission=self.BlobPermissions(read=True), - expiry=datetime.utcnow() + self.timeout, + expiry=pendulum.now(tz="utc").add(self.timeout), content_disposition=f"attachment; filename={filename}", protocol="https", ) diff --git a/atst/domain/invitations.py b/atst/domain/invitations.py index 069be936..f6bfeacb 100644 --- a/atst/domain/invitations.py +++ b/atst/domain/invitations.py @@ -1,5 +1,5 @@ -import datetime from sqlalchemy.orm.exc import NoResultFound +import pendulum from atst.database import db from atst.models import ApplicationInvitation, InvitationStatus, PortfolioInvitation @@ -99,9 +99,7 @@ class BaseInvitations(object): @classmethod def current_expiration_time(cls): - return datetime.datetime.now() + datetime.timedelta( - minutes=cls.EXPIRATION_LIMIT_MINUTES - ) + return pendulum.now(tz="utc").add(minutes=cls.EXPIRATION_LIMIT_MINUTES) @classmethod def _update_status(cls, invite, new_status): diff --git a/atst/domain/task_orders.py b/atst/domain/task_orders.py index 499bccb0..44d6b47e 100644 --- a/atst/domain/task_orders.py +++ b/atst/domain/task_orders.py @@ -1,5 +1,5 @@ -from datetime import datetime from sqlalchemy import or_ +import pendulum from atst.database import db from atst.models.clin import CLIN @@ -41,8 +41,7 @@ class TaskOrders(BaseDomainClass): @classmethod def sign(cls, task_order, 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.commit() diff --git a/atst/domain/users.py b/atst/domain/users.py index e5fdbad7..6ad61cde 100644 --- a/atst/domain/users.py +++ b/atst/domain/users.py @@ -1,6 +1,6 @@ from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.exc import IntegrityError -from datetime import datetime +import pendulum from atst.database import db from atst.models import User @@ -111,7 +111,7 @@ class Users(object): @classmethod def update_last_login(cls, user): - user.last_login = datetime.now() + user.last_login = pendulum.now(tz="utc") db.session.add(user) db.session.commit() diff --git a/atst/filters.py b/atst/filters.py index 84191017..0eb93d2b 100644 --- a/atst/filters.py +++ b/atst/filters.py @@ -1,5 +1,4 @@ import re -import datetime from atst.utils.localization import translate from flask import render_template from jinja2 import contextfilter @@ -54,10 +53,6 @@ def formattedDate(value, formatter="%m/%d/%Y"): return "-" -def dateFromString(value, formatter="%m/%Y"): - return datetime.datetime.strptime(value, formatter) - - def pageWindow(pagination, size=2): page = pagination.page num_pages = pagination.pages @@ -81,7 +76,6 @@ def register_filters(app): app.jinja_env.filters["dollars"] = dollars app.jinja_env.filters["usPhone"] = usPhone app.jinja_env.filters["formattedDate"] = formattedDate - app.jinja_env.filters["dateFromString"] = dateFromString app.jinja_env.filters["pageWindow"] = pageWindow app.jinja_env.filters["renderAuditEvent"] = renderAuditEvent app.jinja_env.filters["withExtraParams"] = with_extra_params diff --git a/atst/models/clin.py b/atst/models/clin.py index 13a63cee..accab107 100644 --- a/atst/models/clin.py +++ b/atst/models/clin.py @@ -9,7 +9,7 @@ from sqlalchemy import ( String, ) from sqlalchemy.orm import relationship -from datetime import date +import pendulum from atst.models.base import Base import atst.models.mixins as mixins @@ -75,5 +75,5 @@ class CLIN(Base, mixins.TimestampsMixin): @property def is_active(self): return ( - self.start_date <= date.today() <= self.end_date + self.start_date <= pendulum.today() <= self.end_date ) and self.task_order.signed_at diff --git a/atst/models/mixins/invites.py b/atst/models/mixins/invites.py index 18916dc4..c87917ba 100644 --- a/atst/models/mixins/invites.py +++ b/atst/models/mixins/invites.py @@ -1,4 +1,4 @@ -import datetime +import pendulum from enum import Enum import secrets @@ -90,7 +90,7 @@ class InvitesMixin(object): @property def is_expired(self): 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 ) diff --git a/atst/routes/portfolios/index.py b/atst/routes/portfolios/index.py index 44cac768..6472fca1 100644 --- a/atst/routes/portfolios/index.py +++ b/atst/routes/portfolios/index.py @@ -1,5 +1,4 @@ -from datetime import datetime - +import pendulum from flask import redirect, render_template, url_for, request as http_request, g from .blueprint import portfolios_bp @@ -56,5 +55,5 @@ def reports(portfolio_id): ), current_obligated_funds=current_obligated_funds, 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 ) diff --git a/atst/routes/users.py b/atst/routes/users.py index ec5557aa..b055bcb6 100644 --- a/atst/routes/users.py +++ b/atst/routes/users.py @@ -1,4 +1,4 @@ -import datetime as dt +import pendulum from flask import Blueprint, render_template, g, request as http_request, redirect from atst.forms.edit_user import EditUserForm from atst.domain.users import Users @@ -23,8 +23,8 @@ def user(): next=next_, form=form, user=user, - mindate=(dt.datetime.now() - dt.timedelta(days=365)), - maxdate=dt.datetime.now(), + mindate=pendulum.now(tz="utc").subtract(days=365), + maxdate=pendulum.now(tz="utc"), ) @@ -44,6 +44,6 @@ def update_user(): form=form, user=user, next=next_url, - mindate=(dt.datetime.now() - dt.timedelta(days=365)), - maxdate=dt.datetime.now(), + mindate=pendulum.now(tz="utc").subtract(days=365), + maxdate=pendulum.now(tz="utc"), ) diff --git a/script/seed_sample.py b/script/seed_sample.py index d1cf1c9e..bc8d13ba 100644 --- a/script/seed_sample.py +++ b/script/seed_sample.py @@ -1,7 +1,7 @@ # Add root application dir to the python path import os import sys -from datetime import timedelta, date +import pendulum import random from faker import Faker from werkzeug.datastructures import FileStorage @@ -170,10 +170,9 @@ def add_members_to_portfolio(portfolio): def add_task_orders_to_portfolio(portfolio): - today = date.today() - future = today + timedelta(days=100) - yesterday = today - timedelta(days=1) - five_days = timedelta(days=5) + today = pendulum.today() + future = today.add(days=100) + yesterday = today.subtract(days=1) def build_pdf(): return {"filename": "sample_task_order.pdf", "object_name": str(uuid4())} @@ -192,13 +191,13 @@ def add_task_orders_to_portfolio(portfolio): clins = [ 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( - 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( - 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( task_order=active_to, diff --git a/tests/conftest.py b/tests/conftest.py index 03d07a12..26b2a1dc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,7 @@ import tests.factories as factories from tests.mocks import PDF_FILENAME, PDF_FILENAME2 from tests.utils import FakeLogger, FakeNotificationSender -from datetime import datetime, timedelta +import pendulum from cryptography.hazmat.primitives.asymmetric import rsa from cryptography import x509 from cryptography.hazmat.backends import default_backend @@ -175,7 +175,7 @@ def extended_financial_verification_data(pdf_upload): return { "funding_type": "RDTE", "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_0003": "13000", "clin_1001": "30000", @@ -214,7 +214,6 @@ def make_x509(): if signer_key is None: signer_key = private_key - one_day = timedelta(1, 0, 0) public_key = private_key.public_key() builder = x509.CertificateBuilder() builder = builder.subject_name( @@ -227,8 +226,8 @@ def make_x509(): builder = builder.add_extension( x509.BasicConstraints(ca=True, path_length=None), critical=True ) - builder = builder.not_valid_before(datetime.today() - (one_day * 2)) - builder = builder.not_valid_after(datetime.today() + (one_day * 30)) + builder = builder.not_valid_before(pendulum.today().subtract(days=2)) + builder = builder.not_valid_after(pendulum.today().add(days=30)) builder = builder.serial_number(x509.random_serial_number()) builder = builder.public_key(public_key) certificate = builder.sign( @@ -249,13 +248,12 @@ def make_crl(): cn="ATAT", expired_serials=None, ): - one_day = timedelta(1, 0, 0) builder = x509.CertificateRevocationListBuilder() builder = builder.issuer_name( x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, cn)]) ) - last_update = datetime.today() + (one_day * last_update_days) - next_update = datetime.today() + (one_day * next_update_days) + last_update = pendulum.today().add(days=last_update_days) + next_update = pendulum.today().add(days=next_update_days) builder = builder.last_update(last_update) builder = builder.next_update(next_update) if expired_serials: diff --git a/tests/domain/test_applications.py b/tests/domain/test_applications.py index 02dd3124..37b167d0 100644 --- a/tests/domain/test_applications.py +++ b/tests/domain/test_applications.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta +import pendulum import pytest from uuid import uuid4 @@ -200,8 +200,8 @@ def test_update_does_not_duplicate_names_within_portfolio(): def test_get_applications_pending_creation(): - now = datetime.now() - later = now + timedelta(minutes=30) + now = pendulum.now(tz="utc") + later = now.add(minutes=30) portfolio1 = PortfolioFactory.create(state="COMPLETED") app_ready = ApplicationFactory.create(portfolio=portfolio1) diff --git a/tests/domain/test_invitations.py b/tests/domain/test_invitations.py index 4d8073be..862f48ca 100644 --- a/tests/domain/test_invitations.py +++ b/tests/domain/test_invitations.py @@ -1,6 +1,6 @@ -import datetime import pytest import re +import pendulum from atst.domain.audit_log import AuditLog from atst.domain.invitations import ( @@ -53,7 +53,7 @@ def test_accept_expired_invitation(): portfolio = PortfolioFactory.create() role = PortfolioRoleFactory.create(portfolio=portfolio) 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( expiration_time=expiration_time, status=InvitationStatus.PENDING, diff --git a/tests/domain/test_task_orders.py b/tests/domain/test_task_orders.py index 93182df0..5999677a 100644 --- a/tests/domain/test_task_orders.py +++ b/tests/domain/test_task_orders.py @@ -1,5 +1,5 @@ import pytest -from datetime import date, datetime, timedelta +import pendulum from decimal import Decimal from atst.domain.exceptions import AlreadyExistsError @@ -15,16 +15,16 @@ def test_create_adds_clins(): { "jedi_clin_type": "JEDI_CLIN_1", "number": "12312", - "start_date": date(2020, 1, 1), - "end_date": date(2021, 1, 1), + "start_date": pendulum.date(2020, 1, 1), + "end_date": pendulum.date(2021, 1, 1), "obligated_amount": Decimal("5000"), "total_amount": Decimal("10000"), }, { "jedi_clin_type": "JEDI_CLIN_1", "number": "12312", - "start_date": date(2020, 1, 1), - "end_date": date(2021, 1, 1), + "start_date": pendulum.date(2020, 1, 1), + "end_date": pendulum.date(2021, 1, 1), "obligated_amount": Decimal("5000"), "total_amount": Decimal("10000"), }, @@ -45,16 +45,16 @@ def test_update_adds_clins(): { "jedi_clin_type": "JEDI_CLIN_1", "number": "12312", - "start_date": date(2020, 1, 1), - "end_date": date(2021, 1, 1), + "start_date": pendulum.date(2020, 1, 1), + "end_date": pendulum.date(2021, 1, 1), "obligated_amount": Decimal("5000"), "total_amount": Decimal("10000"), }, { "jedi_clin_type": "JEDI_CLIN_1", "number": "12312", - "start_date": date(2020, 1, 1), - "end_date": date(2021, 1, 1), + "start_date": pendulum.date(2020, 1, 1), + "end_date": pendulum.date(2021, 1, 1), "obligated_amount": Decimal("5000"), "total_amount": Decimal("10000"), }, @@ -77,16 +77,16 @@ def test_update_does_not_duplicate_clins(): { "jedi_clin_type": "JEDI_CLIN_1", "number": "123", - "start_date": date(2020, 1, 1), - "end_date": date(2021, 1, 1), + "start_date": pendulum.date(2020, 1, 1), + "end_date": pendulum.date(2021, 1, 1), "obligated_amount": Decimal("5000"), "total_amount": Decimal("10000"), }, { "jedi_clin_type": "JEDI_CLIN_1", "number": "111", - "start_date": date(2020, 1, 1), - "end_date": date(2021, 1, 1), + "start_date": pendulum.date(2020, 1, 1), + "end_date": pendulum.date(2021, 1, 1), "obligated_amount": Decimal("5000"), "total_amount": Decimal("10000"), }, @@ -114,9 +114,9 @@ def test_delete_task_order_with_clins(session): def test_task_order_sort_by_status(): - today = date.today() - yesterday = today - timedelta(days=1) - future = today + timedelta(days=100) + today = pendulum.today() + yesterday = today.subtract(days=1) + future = today.add(days=100) initial_to_list = [ # Draft @@ -184,12 +184,12 @@ def test_allows_alphanumeric_number(): def test_get_for_send_task_order_files(): new_to = TaskOrderFactory.create(create_clins=[{}]) updated_to = TaskOrderFactory.create( - create_clins=[{"last_sent_at": datetime(2020, 2, 1)}], - pdf_last_sent_at=datetime(2020, 1, 1), + create_clins=[{"last_sent_at": pendulum.datetime(2020, 2, 1)}], + pdf_last_sent_at=pendulum.datetime(2020, 1, 1), ) sent_to = TaskOrderFactory.create( - create_clins=[{"last_sent_at": datetime(2020, 1, 1)}], - pdf_last_sent_at=datetime(2020, 1, 1), + create_clins=[{"last_sent_at": pendulum.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() diff --git a/tests/domain/test_users.py b/tests/domain/test_users.py index 20bd8266..4f46f5bc 100644 --- a/tests/domain/test_users.py +++ b/tests/domain/test_users.py @@ -1,5 +1,4 @@ import pytest -from datetime import datetime from uuid import uuid4 from atst.domain.users import Users diff --git a/tests/factories.py b/tests/factories.py index aa0a986a..ee548d40 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -3,7 +3,7 @@ import random import string import factory from uuid import uuid4 -import datetime +import pendulum from atst.forms import data from atst.models import * @@ -56,8 +56,8 @@ def _random_date(year_min, year_max, operation): else: inc = random.randrange(year_min, year_max) - return datetime.date( - operation(datetime.date.today().year, inc), + return pendulum.date( + operation(pendulum.today().year, inc), random.randrange(1, 12), random.randrange(1, 28), ) @@ -99,8 +99,7 @@ class UserFactory(Base): citizenship = "United States" designation = "military" date_latest_training = factory.LazyFunction( - lambda: datetime.date.today() - + datetime.timedelta(days=-(random.randrange(1, 365))) + lambda: pendulum.today().add(days=-(random.randrange(1, 365))) ) @classmethod @@ -341,7 +340,7 @@ class CLINFactory(Base): task_order = factory.SubFactory(TaskOrderFactory) number = factory.LazyFunction(random_clin_number) - start_date = datetime.date.today() + start_date = pendulum.today() end_date = factory.LazyFunction(random_future_date) total_amount = factory.LazyFunction(lambda *args: random.randint(50000, 999999)) obligated_amount = factory.LazyFunction(lambda *args: random.randint(100, 50000)) diff --git a/tests/forms/test_task_order.py b/tests/forms/test_task_order.py index ae4fd3c6..7b87d649 100644 --- a/tests/forms/test_task_order.py +++ b/tests/forms/test_task_order.py @@ -1,4 +1,4 @@ -import datetime +import pendulum from dateutil.relativedelta import relativedelta 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(): - invalid_start = datetime.date(2020, 12, 12) - invalid_end = datetime.date(2020, 1, 1) + invalid_start = pendulum.date(2020, 12, 12) + invalid_end = pendulum.date(2020, 1, 1) invalid_clin = factories.CLINFactory.create( 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") in clin_form.start_date.errors ) - valid_start = datetime.date(2020, 1, 1) - valid_end = datetime.date(2020, 12, 12) + valid_start = pendulum.date(2020, 1, 1) + valid_end = pendulum.date(2020, 12, 12) valid_clin = factories.CLINFactory.create( 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( total_amount=0, obligated_amount=1, - start_date=datetime.date(2019, 9, 15), - end_date=datetime.date(2020, 9, 14), + start_date=pendulum.date(2019, 9, 15), + end_date=pendulum.date(2020, 9, 14), ) invalid_clin_form = CLINForm(obj=invalid_clin) assert not invalid_clin_form.validate() @@ -95,8 +95,8 @@ def test_clin_form_dollar_amounts_out_of_range(): invalid_clin = factories.CLINFactory.create( total_amount=-1, obligated_amount=1000000001, - start_date=datetime.date(2019, 9, 15), - end_date=datetime.date(2020, 9, 14), + start_date=pendulum.date(2019, 9, 15), + end_date=pendulum.date(2020, 9, 14), ) invalid_clin_form = CLINForm(obj=invalid_clin) assert not invalid_clin_form.validate() diff --git a/tests/models/test_application_role.py b/tests/models/test_application_role.py index 1807988e..0e3f4f86 100644 --- a/tests/models/test_application_role.py +++ b/tests/models/test_application_role.py @@ -1,4 +1,5 @@ import pytest +import pendulum from atst.domain.permission_sets import PermissionSets from atst.domain.environment_roles import EnvironmentRoles @@ -61,7 +62,7 @@ def test_environment_roles(): 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) assert expired_invite.role.display_status == "invite_expired" diff --git a/tests/models/test_portfolio.py b/tests/models/test_portfolio.py index 71e11bb3..ed6e2513 100644 --- a/tests/models/test_portfolio.py +++ b/tests/models/test_portfolio.py @@ -6,7 +6,6 @@ from tests.factories import ( random_future_date, random_past_date, ) -import datetime import pendulum from decimal import Decimal import pytest @@ -83,7 +82,7 @@ def test_funding_duration(session): portfolio=portfolio, signed_at=random_past_date(), 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 ( portfolio.days_to_funding_expiration - == (funding_end_date - datetime.date.today()).days + == (funding_end_date - pendulum.today()).days ) # empty portfolio @@ -121,8 +120,8 @@ def test_active_task_orders(session): signed_at=random_past_date(), create_clins=[ { - "start_date": datetime.date(2019, 1, 1), - "end_date": datetime.date(2019, 10, 31), + "start_date": pendulum.date(2019, 1, 1), + "end_date": pendulum.date(2019, 10, 31), } ], ) diff --git a/tests/models/test_portfolio_invitations.py b/tests/models/test_portfolio_invitations.py index faf9365d..dc033c4f 100644 --- a/tests/models/test_portfolio_invitations.py +++ b/tests/models/test_portfolio_invitations.py @@ -1,4 +1,4 @@ -import datetime +import pendulum from atst.models import InvitationStatus, PortfolioRoleStatus @@ -17,7 +17,7 @@ def test_expired_invite_is_not_revokable(): portfolio=portfolio, user=user, status=PortfolioRoleStatus.PENDING ) invite = PortfolioInvitationFactory.create( - expiration_time=datetime.datetime.now() - datetime.timedelta(minutes=60), + expiration_time=pendulum.now(tz="utc").subtract(minutes=60), role=portfolio_role, ) assert not invite.is_revokable diff --git a/tests/models/test_portfolio_role.py b/tests/models/test_portfolio_role.py index 5a12d55e..e3d08d26 100644 --- a/tests/models/test_portfolio_role.py +++ b/tests/models/test_portfolio_role.py @@ -1,5 +1,5 @@ import pytest -import datetime +import pendulum from atst.domain.environments import Environments from atst.domain.portfolios import Portfolios @@ -204,7 +204,7 @@ def test_status_when_invitation_is_expired(): PortfolioInvitationFactory.create( role=portfolio_role, 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" diff --git a/tests/models/test_task_order.py b/tests/models/test_task_order.py index 27512bf1..0b03cbf8 100644 --- a/tests/models/test_task_order.py +++ b/tests/models/test_task_order.py @@ -1,6 +1,6 @@ from werkzeug.datastructures import FileStorage import pytest -from datetime import date +import pendulum from unittest.mock import patch, PropertyMock import pendulum @@ -13,11 +13,11 @@ from tests.mocks import PDF_FILENAME def test_period_of_performance_is_first_to_last_clin(): - start_date = date(2019, 6, 6) - end_date = date(2020, 6, 6) + start_date = pendulum.date(2019, 6, 6) + end_date = pendulum.date(2020, 6, 6) - intermediate_start_date = date(2019, 7, 1) - intermediate_end_date = date(2020, 3, 1) + intermediate_start_date = pendulum.date(2019, 7, 1) + intermediate_end_date = pendulum.date(2020, 3, 1) task_order = TaskOrderFactory.create( clins=[ diff --git a/tests/models/test_user.py b/tests/models/test_user.py index f4e29235..7dbeb6f4 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -1,6 +1,6 @@ import pytest from sqlalchemy.exc import InternalError -from datetime import datetime +import pendulum from atst.database import db 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): user = UserFactory.create() - user.last_login = datetime.now() + user.last_login = pendulum.now(tz="utc") db.session.add(user) db.session.commit() assert "Audit Event update" not in mock_logger.messages diff --git a/tests/routes/applications/test_settings.py b/tests/routes/applications/test_settings.py index 04d59f03..415dab4c 100644 --- a/tests/routes/applications/test_settings.py +++ b/tests/routes/applications/test_settings.py @@ -1,4 +1,4 @@ -import datetime +import pendulum import uuid from unittest.mock import Mock, patch @@ -613,7 +613,7 @@ def test_filter_environment_roles(): user = UserFactory.create() # 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 - yesterday = datetime.date.today() - datetime.timedelta(days=1) + yesterday = pendulum.today().subtract(days=1) invite = ApplicationInvitationFactory.create( user=user, time_created=yesterday, email="original@example.com" ) diff --git a/tests/routes/portfolios/test_invitations.py b/tests/routes/portfolios/test_invitations.py index f54fca3b..f8872c08 100644 --- a/tests/routes/portfolios/test_invitations.py +++ b/tests/routes/portfolios/test_invitations.py @@ -1,4 +1,4 @@ -import datetime +import pendulum from unittest.mock import Mock from flask import url_for @@ -123,7 +123,7 @@ def test_user_accepts_expired_invite(client, user_session): user_id=user.id, role=ws_role, 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) response = client.get( @@ -143,7 +143,7 @@ def test_revoke_invitation(client, user_session): user_id=user.id, role=ws_role, 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) response = client.post( @@ -169,7 +169,7 @@ def test_user_can_only_revoke_invites_in_their_portfolio(client, user_session): user_id=user.id, role=portfolio_role, 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) response = client.post( @@ -199,7 +199,7 @@ def test_user_can_only_resend_invites_in_their_portfolio( user_id=user.id, role=portfolio_role, 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) response = client.post( diff --git a/tests/routes/task_orders/test_index.py b/tests/routes/task_orders/test_index.py index e9b81f7a..d3bd672d 100644 --- a/tests/routes/task_orders/test_index.py +++ b/tests/routes/task_orders/test_index.py @@ -1,7 +1,5 @@ -from datetime import date from flask import url_for import pytest -from datetime import timedelta, date from atst.domain.permission_sets import PermissionSets from atst.domain.task_orders import TaskOrders diff --git a/tests/routes/task_orders/test_new.py b/tests/routes/task_orders/test_new.py index 9929a992..3f27c19f 100644 --- a/tests/routes/task_orders/test_new.py +++ b/tests/routes/task_orders/test_new.py @@ -1,6 +1,6 @@ import pytest from flask import url_for, get_flashed_messages -from datetime import timedelta, date +import pendulum from uuid import uuid4 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 - active_start_date = date.today() - timedelta(days=1) + active_start_date = pendulum.today().subtract(days=1) active_task_order = TaskOrderFactory(portfolio=task_order.portfolio) CLINFactory(task_order=active_task_order, start_date=active_start_date) 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 - upcoming_start_date = date.today() + timedelta(days=1) + upcoming_start_date = pendulum.today().add(days=1) upcoming_task_order = TaskOrderFactory(portfolio=task_order.portfolio) CLINFactory(task_order=upcoming_task_order, start_date=upcoming_start_date) assert upcoming_task_order.status == TaskOrderStatus.UNSIGNED diff --git a/tests/test_auth.py b/tests/test_auth.py index 8444c14f..88381218 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -3,11 +3,11 @@ from urllib.parse import urlparse import pytest from datetime import datetime +import pendulum from flask import session, url_for from cryptography.hazmat.primitives.serialization import Encoding from atst.domain.users import Users -from atst.domain.permission_sets import PermissionSets from atst.domain.exceptions import NotFoundError from atst.domain.authnid.crl import CRLInvalidException 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): - last_login = datetime.now() + last_login = pendulum.now(tz="utc") user = UserFactory.create(last_login=last_login) monkeypatch.setattr( "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( "atst.domain.authnid.AuthenticationContext.get_user", lambda *args: user ) - response = _login(client) + _login(client) assert session["last_login"] assert user.last_login > session["last_login"] assert isinstance(session["last_login"], datetime)