diff --git a/.pylintrc b/.pylintrc index 2612f2e7..09eae129 100644 --- a/.pylintrc +++ b/.pylintrc @@ -52,6 +52,7 @@ disable=all enable=import-error, import-self, + cyclic-import, reimported, misplaced-future, relative-import, diff --git a/atst/domain/permission_sets.py b/atst/domain/permission_sets.py index 727b34df..8d110dc3 100644 --- a/atst/domain/permission_sets.py +++ b/atst/domain/permission_sets.py @@ -1,7 +1,8 @@ from sqlalchemy.orm.exc import NoResultFound from atst.database import db -from atst.models import PermissionSet, Permissions +from atst.models.permissions import Permissions +from atst.models.permission_set import PermissionSet from .exceptions import NotFoundError diff --git a/atst/models/__init__.py b/atst/models/__init__.py index 7cee8de7..dc7b2f8a 100644 --- a/atst/models/__init__.py +++ b/atst/models/__init__.py @@ -1,8 +1,4 @@ -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() - - +from .base import Base from .application import Application from .application_invitation import ApplicationInvitation from .application_role import ApplicationRole, Status as ApplicationRoleStatus diff --git a/atst/models/application.py b/atst/models/application.py index f79812c6..a25824d6 100644 --- a/atst/models/application.py +++ b/atst/models/application.py @@ -1,7 +1,7 @@ from sqlalchemy import and_, Column, ForeignKey, String from sqlalchemy.orm import relationship, synonym -from atst.models import Base +from atst.models.base import Base from atst.models.application_role import ApplicationRole from atst.models.environment import Environment from atst.models import mixins diff --git a/atst/models/application_invitation.py b/atst/models/application_invitation.py index 328ac59e..d24cc54d 100644 --- a/atst/models/application_invitation.py +++ b/atst/models/application_invitation.py @@ -2,11 +2,13 @@ from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship, backref -from atst.models import Base -from atst.models.mixins import TimestampsMixin, AuditableMixin, InvitesMixin +from atst.models.base import Base +import atst.models.mixins as mixins -class ApplicationInvitation(Base, TimestampsMixin, AuditableMixin, InvitesMixin): +class ApplicationInvitation( + Base, mixins.TimestampsMixin, mixins.AuditableMixin, mixins.InvitesMixin +): __tablename__ = "application_invitations" application_role_id = Column( diff --git a/atst/models/application_role.py b/atst/models/application_role.py index 7e709f5f..f0e20be1 100644 --- a/atst/models/application_role.py +++ b/atst/models/application_role.py @@ -5,10 +5,11 @@ from sqlalchemy.orm import relationship from sqlalchemy.event import listen from atst.utils import first_or_none -from atst.models import Base, mixins +from atst.models.base import Base +import atst.models.mixins as mixins +import atst.models.types as types from atst.models.environment_role import EnvironmentRole from atst.models.mixins.auditable import record_permission_sets_updates -from .types import Id class Status(Enum): @@ -36,7 +37,7 @@ class ApplicationRole( ): __tablename__ = "application_roles" - id = Id() + id = types.Id() application_id = Column( UUID(as_uuid=True), ForeignKey("applications.id"), index=True, nullable=False ) diff --git a/atst/models/attachment.py b/atst/models/attachment.py index 284219fd..ca0fe103 100644 --- a/atst/models/attachment.py +++ b/atst/models/attachment.py @@ -2,7 +2,9 @@ from sqlalchemy import Column, String from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm.exc import NoResultFound -from atst.models import Base, types, mixins +from atst.models.base import Base +import atst.models.mixins as mixins +import atst.models.types as types from atst.database import db from atst.domain.exceptions import NotFoundError diff --git a/atst/models/audit_event.py b/atst/models/audit_event.py index 90c69ee1..bd069550 100644 --- a/atst/models/audit_event.py +++ b/atst/models/audit_event.py @@ -2,7 +2,8 @@ from sqlalchemy import String, Column, ForeignKey, inspect from sqlalchemy.dialects.postgresql import UUID, JSONB from sqlalchemy.orm import relationship -from atst.models import Base, types +from atst.models.base import Base +import atst.models.types as types from atst.models.mixins.timestamps import TimestampsMixin diff --git a/atst/models/base.py b/atst/models/base.py new file mode 100644 index 00000000..860e5425 --- /dev/null +++ b/atst/models/base.py @@ -0,0 +1,3 @@ +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() diff --git a/atst/models/clin.py b/atst/models/clin.py index 6107a1f3..e376da9c 100644 --- a/atst/models/clin.py +++ b/atst/models/clin.py @@ -2,7 +2,9 @@ from enum import Enum from sqlalchemy import Column, Date, Enum as SQLAEnum, ForeignKey, Numeric, String from sqlalchemy.orm import relationship -from atst.models import Base, mixins, types +from atst.models.base import Base +import atst.models.mixins as mixins +import atst.models.types as types class JEDICLINType(Enum): diff --git a/atst/models/environment.py b/atst/models/environment.py index e42681a2..14314cb1 100644 --- a/atst/models/environment.py +++ b/atst/models/environment.py @@ -3,9 +3,9 @@ from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import JSONB from enum import Enum -from atst.models import Base -from atst.models.types import Id -from atst.models import mixins +from atst.models.base import Base +import atst.models.mixins as mixins +import atst.models.types as types class Environment( @@ -13,7 +13,7 @@ class Environment( ): __tablename__ = "environments" - id = Id() + id = types.Id() name = Column(String, nullable=False) application_id = Column(ForeignKey("applications.id"), nullable=False) diff --git a/atst/models/environment_role.py b/atst/models/environment_role.py index 613ade78..97b2988a 100644 --- a/atst/models/environment_role.py +++ b/atst/models/environment_role.py @@ -3,7 +3,9 @@ from sqlalchemy import Index, ForeignKey, Column, String, TIMESTAMP, Enum as SQL from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship -from atst.models import Base, types, mixins +from atst.models.base import Base +import atst.models.mixins as mixins +import atst.models.types as types class CSPRole(Enum): diff --git a/atst/models/job_failure.py b/atst/models/job_failure.py index 50d553a1..c7ca9482 100644 --- a/atst/models/job_failure.py +++ b/atst/models/job_failure.py @@ -1,7 +1,7 @@ from sqlalchemy import Column, ForeignKey -from atst.models import Base -from atst.models import mixins +from atst.models.base import Base +import atst.models.mixins as mixins class EnvironmentJobFailure(Base, mixins.JobFailureMixin): diff --git a/atst/models/notification_recipient.py b/atst/models/notification_recipient.py index 72b55cbe..ab5a2d66 100644 --- a/atst/models/notification_recipient.py +++ b/atst/models/notification_recipient.py @@ -1,6 +1,8 @@ from sqlalchemy import String, Column -from atst.models import Base, types, mixins +from atst.models.base import Base +import atst.models.types as types +import atst.models.mixins as mixins class NotificationRecipient(Base, mixins.TimestampsMixin): diff --git a/atst/models/permission_set.py b/atst/models/permission_set.py index 1818fbb7..9d07e645 100644 --- a/atst/models/permission_set.py +++ b/atst/models/permission_set.py @@ -1,7 +1,9 @@ from sqlalchemy import String, Column from sqlalchemy.dialects.postgresql import ARRAY -from atst.models import Base, types, mixins +from atst.models.base import Base +import atst.models.mixins as mixins +import atst.models.types as types class PermissionSet(Base, mixins.TimestampsMixin): diff --git a/atst/models/portfolio.py b/atst/models/portfolio.py index 61ec6375..d749e470 100644 --- a/atst/models/portfolio.py +++ b/atst/models/portfolio.py @@ -1,9 +1,11 @@ -from sqlalchemy import and_, Column, String +from sqlalchemy import Column, String from sqlalchemy.orm import relationship from sqlalchemy.types import ARRAY from itertools import chain -from atst.models import Base, Application, mixins, types +from atst.models.base import Base +import atst.models.types as types +import atst.models.mixins as mixins from atst.models.portfolio_role import PortfolioRole, Status as PortfolioRoleStatus from atst.domain.permission_sets import PermissionSets from atst.utils import first_or_none @@ -31,7 +33,7 @@ class Portfolio( applications = relationship( "Application", back_populates="portfolio", - primaryjoin=and_(Application.portfolio_id == id, Application.deleted == False), + primaryjoin="and_(Application.portfolio_id == Portfolio.id, Application.deleted == False)", ) roles = relationship("PortfolioRole") diff --git a/atst/models/portfolio_invitation.py b/atst/models/portfolio_invitation.py index a6e070d4..55d895c6 100644 --- a/atst/models/portfolio_invitation.py +++ b/atst/models/portfolio_invitation.py @@ -2,11 +2,13 @@ from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship, backref -from atst.models import Base -from atst.models.mixins import TimestampsMixin, AuditableMixin, InvitesMixin +from atst.models.base import Base +import atst.models.mixins as mixins -class PortfolioInvitation(Base, TimestampsMixin, InvitesMixin, AuditableMixin): +class PortfolioInvitation( + Base, mixins.TimestampsMixin, mixins.InvitesMixin, mixins.AuditableMixin +): __tablename__ = "portfolio_invitations" portfolio_role_id = Column( diff --git a/atst/models/portfolio_role.py b/atst/models/portfolio_role.py index c88fd88b..500a0ddd 100644 --- a/atst/models/portfolio_role.py +++ b/atst/models/portfolio_role.py @@ -4,8 +4,9 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship from sqlalchemy.event import listen -from atst.models import Base, mixins -from .types import Id +from atst.models.base import Base +import atst.models.types as types +import atst.models.mixins as mixins from atst.utils import first_or_none from atst.models.mixins.auditable import record_permission_sets_updates @@ -41,7 +42,7 @@ class PortfolioRole( ): __tablename__ = "portfolio_roles" - id = Id() + id = types.Id() portfolio_id = Column( UUID(as_uuid=True), ForeignKey("portfolios.id"), index=True, nullable=False ) diff --git a/atst/models/task_order.py b/atst/models/task_order.py index 3fe3d3e3..c79d3b83 100644 --- a/atst/models/task_order.py +++ b/atst/models/task_order.py @@ -5,7 +5,10 @@ from sqlalchemy import Column, DateTime, ForeignKey, String from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import relationship -from atst.models import Attachment, Base, mixins, types +from atst.models.base import Base +import atst.models.types as types +import atst.models.mixins as mixins +from atst.models.attachment import Attachment from atst.utils.clock import Clock diff --git a/atst/models/user.py b/atst/models/user.py index ea285e4c..4ba23895 100644 --- a/atst/models/user.py +++ b/atst/models/user.py @@ -1,9 +1,11 @@ -from sqlalchemy import and_, String, ForeignKey, Column, Date, Boolean, Table, TIMESTAMP +from sqlalchemy import String, ForeignKey, Column, Date, Boolean, Table, TIMESTAMP from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.event import listen -from atst.models import Base, ApplicationRole, types, mixins +from atst.models.base import Base +import atst.models.types as types +import atst.models.mixins as mixins from atst.models.portfolio_invitation import PortfolioInvitation from atst.models.application_invitation import ApplicationInvitation from atst.models.mixins.auditable import ( @@ -35,9 +37,7 @@ class User( application_roles = relationship( "ApplicationRole", backref="user", - primaryjoin=and_( - ApplicationRole.user_id == id, ApplicationRole.deleted == False - ), + primaryjoin="and_(ApplicationRole.user_id == User.id, ApplicationRole.deleted == False)", ) portfolio_invitations = relationship( diff --git a/atst/routes/applications/__init__.py b/atst/routes/applications/__init__.py index 6a98e188..8e09c1ed 100644 --- a/atst/routes/applications/__init__.py +++ b/atst/routes/applications/__init__.py @@ -1,18 +1,14 @@ -from flask import Blueprint, current_app as app, g, redirect, url_for - -applications_bp = Blueprint("applications", __name__) +from flask import current_app as app, g, redirect, url_for from . import index from . import new from . import settings from . import invitations +from .blueprint import applications_bp from atst.domain.environment_roles import EnvironmentRoles from atst.domain.exceptions import UnauthorizedError from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.models.permissions import Permissions -from atst.utils.context_processors import portfolio as portfolio_context_processor - -applications_bp.context_processor(portfolio_context_processor) def wrap_environment_role_lookup(user, environment_id=None, **kwargs): diff --git a/atst/routes/applications/blueprint.py b/atst/routes/applications/blueprint.py new file mode 100644 index 00000000..24e189f5 --- /dev/null +++ b/atst/routes/applications/blueprint.py @@ -0,0 +1,6 @@ +from flask import Blueprint + +from atst.utils.context_processors import portfolio as portfolio_context_processor + +applications_bp = Blueprint("applications", __name__) +applications_bp.context_processor(portfolio_context_processor) diff --git a/atst/routes/applications/index.py b/atst/routes/applications/index.py index e89b1efa..dc9f4c9c 100644 --- a/atst/routes/applications/index.py +++ b/atst/routes/applications/index.py @@ -1,6 +1,6 @@ from flask import render_template -from . import applications_bp +from .blueprint import applications_bp from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.models.permissions import Permissions diff --git a/atst/routes/applications/invitations.py b/atst/routes/applications/invitations.py index 3a7c80c3..1fd69c12 100644 --- a/atst/routes/applications/invitations.py +++ b/atst/routes/applications/invitations.py @@ -1,6 +1,6 @@ from flask import redirect, url_for, g -from . import applications_bp +from .blueprint import applications_bp from atst.domain.invitations import ApplicationInvitations diff --git a/atst/routes/applications/new.py b/atst/routes/applications/new.py index 0eecf778..8ee53473 100644 --- a/atst/routes/applications/new.py +++ b/atst/routes/applications/new.py @@ -1,6 +1,6 @@ from flask import redirect, render_template, request as http_request, url_for, g -from . import applications_bp +from .blueprint import applications_bp from atst.domain.applications import Applications from atst.domain.portfolios import Portfolios from atst.forms.application import NameAndDescriptionForm, EnvironmentsForm diff --git a/atst/routes/applications/settings.py b/atst/routes/applications/settings.py index 8b92ca0f..f695e4b0 100644 --- a/atst/routes/applications/settings.py +++ b/atst/routes/applications/settings.py @@ -1,6 +1,6 @@ from flask import redirect, render_template, request as http_request, url_for, g -from . import applications_bp +from .blueprint import applications_bp from atst.domain.exceptions import AlreadyExistsError from atst.domain.environments import Environments from atst.domain.applications import Applications diff --git a/atst/routes/portfolios/__init__.py b/atst/routes/portfolios/__init__.py index bceb9100..56f74a24 100644 --- a/atst/routes/portfolios/__init__.py +++ b/atst/routes/portfolios/__init__.py @@ -1,12 +1,7 @@ -from flask import Blueprint, request as http_request, g, render_template +from flask import request as http_request, g, render_template from operator import attrgetter -portfolios_bp = Blueprint("portfolios", __name__) - from . import index from . import invitations from . import admin -from atst.utils.context_processors import portfolio as portfolio_context_processor - - -portfolios_bp.context_processor(portfolio_context_processor) +from .blueprint import portfolios_bp diff --git a/atst/routes/portfolios/admin.py b/atst/routes/portfolios/admin.py index 187ca714..affcaed4 100644 --- a/atst/routes/portfolios/admin.py +++ b/atst/routes/portfolios/admin.py @@ -1,6 +1,6 @@ from flask import render_template, request as http_request, g, redirect, url_for -from . import portfolios_bp +from .blueprint import portfolios_bp from atst.domain.portfolios import Portfolios from atst.domain.portfolio_roles import PortfolioRoles from atst.models.portfolio_role import Status as PortfolioRoleStatus diff --git a/atst/routes/portfolios/blueprint.py b/atst/routes/portfolios/blueprint.py new file mode 100644 index 00000000..703a2515 --- /dev/null +++ b/atst/routes/portfolios/blueprint.py @@ -0,0 +1,5 @@ +from flask import Blueprint +from atst.utils.context_processors import portfolio as portfolio_context_processor + +portfolios_bp = Blueprint("portfolios", __name__) +portfolios_bp.context_processor(portfolio_context_processor) diff --git a/atst/routes/portfolios/index.py b/atst/routes/portfolios/index.py index 851d5ae8..9c2ec1a0 100644 --- a/atst/routes/portfolios/index.py +++ b/atst/routes/portfolios/index.py @@ -2,7 +2,7 @@ from datetime import date, timedelta from flask import redirect, render_template, url_for, request as http_request, g -from . import portfolios_bp +from .blueprint import portfolios_bp from atst.forms.portfolio import PortfolioCreationForm from atst.domain.reports import Reports from atst.domain.portfolios import Portfolios diff --git a/atst/routes/portfolios/invitations.py b/atst/routes/portfolios/invitations.py index 16fc0bf2..8f2e4701 100644 --- a/atst/routes/portfolios/invitations.py +++ b/atst/routes/portfolios/invitations.py @@ -1,6 +1,6 @@ from flask import g, redirect, url_for, render_template, request as http_request -from . import portfolios_bp +from .blueprint import portfolios_bp from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.domain.exceptions import AlreadyExistsError from atst.domain.invitations import PortfolioInvitations diff --git a/atst/routes/task_orders/__init__.py b/atst/routes/task_orders/__init__.py index 13f37099..c89181a0 100644 --- a/atst/routes/task_orders/__init__.py +++ b/atst/routes/task_orders/__init__.py @@ -1,10 +1,4 @@ -from flask import Blueprint - -task_orders_bp = Blueprint("task_orders", __name__) - from . import index from . import new from . import downloads -from atst.utils.context_processors import portfolio as portfolio_context_processor - -task_orders_bp.context_processor(portfolio_context_processor) +from .blueprint import task_orders_bp diff --git a/atst/routes/task_orders/blueprint.py b/atst/routes/task_orders/blueprint.py new file mode 100644 index 00000000..4222befd --- /dev/null +++ b/atst/routes/task_orders/blueprint.py @@ -0,0 +1,5 @@ +from flask import Blueprint +from atst.utils.context_processors import portfolio as portfolio_context_processor + +task_orders_bp = Blueprint("task_orders", __name__) +task_orders_bp.context_processor(portfolio_context_processor) diff --git a/atst/routes/task_orders/downloads.py b/atst/routes/task_orders/downloads.py index 7cc1b22b..8add1db2 100644 --- a/atst/routes/task_orders/downloads.py +++ b/atst/routes/task_orders/downloads.py @@ -1,6 +1,6 @@ from flask import Response, current_app as app -from . import task_orders_bp +from .blueprint import task_orders_bp from atst.domain.task_orders import TaskOrders from atst.domain.exceptions import NotFoundError from atst.domain.authz.decorator import user_can_access_decorator as user_can diff --git a/atst/routes/task_orders/index.py b/atst/routes/task_orders/index.py index 7ce6fc3e..a58ea8da 100644 --- a/atst/routes/task_orders/index.py +++ b/atst/routes/task_orders/index.py @@ -1,6 +1,6 @@ from flask import g, render_template, url_for, redirect -from . import task_orders_bp +from .blueprint import task_orders_bp from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.domain.portfolios import Portfolios from atst.domain.task_orders import TaskOrders diff --git a/atst/routes/task_orders/new.py b/atst/routes/task_orders/new.py index 5cc51d9c..24460204 100644 --- a/atst/routes/task_orders/new.py +++ b/atst/routes/task_orders/new.py @@ -8,7 +8,7 @@ from flask import ( jsonify, ) -from . import task_orders_bp +from .blueprint import task_orders_bp from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.domain.exceptions import NoAccessError from atst.domain.task_orders import TaskOrders diff --git a/atst/utils/notification_sender.py b/atst/utils/notification_sender.py index 9b8e8a25..430f4b1c 100644 --- a/atst/utils/notification_sender.py +++ b/atst/utils/notification_sender.py @@ -2,7 +2,7 @@ from sqlalchemy import select from atst.jobs import send_notification_mail from atst.database import db -from atst.models import NotificationRecipient +from atst.models.notification_recipient import NotificationRecipient class NotificationSender(object):