Merge pull request #621 from GSA/API-331_Create_Properties_On_Org_Model

Api 331 create properties on org model
This commit is contained in:
Carlo Costino
2023-11-22 13:48:06 -05:00
committed by GitHub
4 changed files with 150 additions and 4 deletions
+50 -2
View File
@@ -1,6 +1,7 @@
import datetime
import itertools
import uuid
from enum import Enum
from flask import current_app, url_for
from notifications_utils.clients.encryption.encryption_client import EncryptionError
@@ -433,6 +434,32 @@ class Organization(db.Model):
def domain_list(self):
return [domain.domain for domain in self.domains]
@property
def agreement(self):
try:
active_agreements = [
agreement
for agreement in self.agreements
if agreement.status == AgreementStatus.ACTIVE
]
return active_agreements[0]
except IndexError:
return None
@property
def agreement_active(self):
try:
return self.agreement.status == AgreementStatus.ACTIVE
except AttributeError:
return False
@property
def has_mou(self):
try:
return self.agreement.type == AgreementType.MOU
except AttributeError:
return False
def serialize(self):
return {
"id": str(self.id),
@@ -2353,14 +2380,34 @@ class WebauthnCredential(db.Model):
}
class AgreementType(Enum):
MOU = "MOU"
IAA = "IAA"
class AgreementStatus(Enum):
ACTIVE = "active"
EXPIRED = "expired"
class Agreement(db.Model):
__tablename__ = "agreements"
id = db.Column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=False
)
type = db.Column(db.String(3), nullable=False, unique=True, index=True)
type = db.Column(
db.Enum(AgreementType, name="agreement_types"),
index=False,
unique=False,
nullable=False,
)
partner_name = db.Column(db.String(255), nullable=False, unique=True, index=True)
status = db.Column(db.String(255), nullable=False, unique=True, index=True)
status = db.Column(
db.Enum(AgreementStatus, name="agreement_statuses"),
index=False,
unique=False,
nullable=False,
)
start_time = db.Column(db.DateTime, nullable=True)
end_time = db.Column(db.DateTime, nullable=True)
url = db.Column(db.String(255), nullable=False, unique=True, index=True)
@@ -2370,6 +2417,7 @@ class Agreement(db.Model):
db.ForeignKey("organization.id"),
nullable=True,
)
organization = db.relationship("Organization", backref="agreements")
def serialize(self):
return {
@@ -0,0 +1,53 @@
"""
Revision ID: eb7747053d5d
Revises: 0404_expire_invites
Create Date: 2023-11-17 15:39:45.470089
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = "0406_adjust_agreement_model"
down_revision = "0405_add_preferred_timezone"
agreement_type_name = "agreement_types"
agreement_type_options = ("MOU", "IAA")
agreement_types = sa.Enum(*agreement_type_options, name=agreement_type_name)
agreement_status_name = "agreement_statuses"
agreement_status_options = ("active", "expired")
agreement_statuses = sa.Enum(*agreement_status_options, name=agreement_status_name)
def upgrade():
agreement_types.create(op.get_bind())
op.execute(
f"ALTER TABLE agreements ALTER COLUMN type TYPE {agreement_type_name} using type::text::{agreement_type_name}"
)
agreement_statuses.create(op.get_bind())
op.execute(
f"ALTER TABLE agreements ALTER COLUMN status TYPE {agreement_status_name} using status::text::{agreement_status_name}"
)
def downgrade():
op.alter_column(
"agreements",
"status",
existing_type=agreement_statuses,
type_=sa.VARCHAR(length=255),
existing_nullable=False,
)
op.execute(f"DROP TYPE {agreement_status_name}")
op.alter_column(
"agreements",
"type",
existing_type=agreement_types,
type_=sa.VARCHAR(length=3),
existing_nullable=False,
)
op.execute(f"DROP TYPE {agreement_type_name}")
+4 -2
View File
@@ -133,16 +133,18 @@ def test_should_delete_all_invitations_more_than_one_day_old(
def test_should_not_delete_invitations_less_than_two_days_old(
sample_user, sample_service
):
two_days = timedelta(days=2)
one_second = timedelta(seconds=1)
make_invitation(
sample_user,
sample_service,
age=timedelta(hours=47, minutes=59, seconds=59),
age=two_days - one_second, # Not quite two days
email_address="valid@2.com",
)
make_invitation(
sample_user,
sample_service,
age=timedelta(hours=48),
age=two_days,
email_address="expired@1.com",
)
+43
View File
@@ -15,6 +15,8 @@ from app.models import (
NOTIFICATION_TECHNICAL_FAILURE,
SMS_TYPE,
Agreement,
AgreementStatus,
AgreementType,
AnnualBilling,
Notification,
NotificationHistory,
@@ -28,6 +30,7 @@ from app.models import (
from tests.app.db import (
create_inbound_number,
create_notification,
create_organization,
create_rate,
create_reply_to_email,
create_service,
@@ -412,6 +415,46 @@ def test_rate_str():
assert rate.__str__() == "1.5 sms 2023-01-01 00:00:00"
@pytest.mark.parametrize(
["agreement_type", "expected"],
(
(AgreementType.IAA, False),
(AgreementType.MOU, True),
),
)
def test_organization_agreement_mou(notify_db_session, agreement_type, expected):
now = datetime.utcnow()
agree = Agreement()
agree.id = "whatever"
agree.start_time = now
agree.end_time = now
agree.status = AgreementStatus.ACTIVE
agree.type = agreement_type
organization = create_organization(name="Something")
organization.agreements.append(agree)
assert organization.has_mou == expected
@pytest.mark.parametrize(
["agreement_status", "expected"],
(
(AgreementStatus.EXPIRED, False),
(AgreementStatus.ACTIVE, True),
),
)
def test_organization_agreement_active(notify_db_session, agreement_status, expected):
now = datetime.utcnow()
agree = Agreement()
agree.id = "whatever"
agree.start_time = now
agree.end_time = now
agree.status = agreement_status
agree.type = AgreementType.IAA
organization = create_organization(name="Something")
organization.agreements.append(agree)
assert organization.agreement_active == expected
def test_agreement_serialize():
agree = Agreement()
agree.id = "abc"