Skip to content

Commit

Permalink
Merge branch 'master' into feat/retry
Browse files Browse the repository at this point in the history
  • Loading branch information
wssheldon authored Jul 1, 2024
2 parents 50dba84 + 85ea260 commit d299946
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 4 deletions.
25 changes: 25 additions & 0 deletions src/dispatch/case/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
Table,
UniqueConstraint,
)
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from sqlalchemy_utils import TSVectorType, observes

from dispatch.case_cost.models import (
CaseCostRead,
CaseCostUpdate,
)
from dispatch.case.priority.models import CasePriorityBase, CasePriorityCreate, CasePriorityRead
from dispatch.case.severity.models import CaseSeverityBase, CaseSeverityCreate, CaseSeverityRead
from dispatch.case.type.models import CaseTypeBase, CaseTypeCreate, CaseTypeRead
Expand Down Expand Up @@ -167,6 +172,15 @@ class Case(Base, TimeStampMixin, ProjectMixin):

ticket = relationship("Ticket", uselist=False, backref="case", cascade="all, delete-orphan")

# resources
case_costs = relationship(
"CaseCost",
backref="case",
cascade="all, delete-orphan",
lazy="subquery",
order_by="CaseCost.created_at",
)

@observes("participants")
def participant_observer(self, participants):
self.participants_team = Counter(p.team for p in participants).most_common(1)[0][0]
Expand All @@ -184,6 +198,14 @@ def has_thread(self) -> bool:
return False
return True if self.conversation.thread_id else False

@hybrid_property
def total_cost(self):
total_cost = 0
if self.case_costs:
for cost in self.case_costs:
total_cost += cost.amount
return total_cost


class SignalRead(DispatchBase):
id: PrimaryKey
Expand Down Expand Up @@ -249,6 +271,7 @@ class CaseCreate(CaseBase):
class CaseReadMinimal(CaseBase):
id: PrimaryKey
assignee: Optional[ParticipantReadMinimal]
case_costs: Optional[List[CaseCostRead]] = []
case_priority: CasePriorityRead
case_severity: CaseSeverityRead
case_type: CaseTypeRead
Expand All @@ -271,6 +294,7 @@ class CaseReadMinimal(CaseBase):
class CaseRead(CaseBase):
id: PrimaryKey
assignee: Optional[ParticipantRead]
case_costs: Optional[List[CaseCostRead]] = []
case_priority: CasePriorityRead
case_severity: CaseSeverityRead
case_type: CaseTypeRead
Expand Down Expand Up @@ -300,6 +324,7 @@ class CaseRead(CaseBase):

class CaseUpdate(CaseBase):
assignee: Optional[ParticipantUpdate]
case_costs: Optional[List[CaseCostUpdate]] = []
case_priority: Optional[CasePriorityBase]
case_severity: Optional[CaseSeverityBase]
case_type: Optional[CaseTypeBase]
Expand Down
8 changes: 8 additions & 0 deletions src/dispatch/case/type/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from sqlalchemy.sql.schema import UniqueConstraint
from sqlalchemy_utils import TSVectorType

from dispatch.cost_model.models import CostModelRead
from dispatch.database.core import Base, ensure_unique_default_per_project
from dispatch.enums import Visibility
from dispatch.models import DispatchBase, NameStr, Pagination, PrimaryKey, ProjectMixin
Expand Down Expand Up @@ -40,6 +41,12 @@ class CaseType(ProjectMixin, Base):
incident_type_id = Column(Integer, ForeignKey("incident_type.id"))
incident_type = relationship("IncidentType", foreign_keys=[incident_type_id])

cost_model_id = Column(Integer, ForeignKey("cost_model.id"), nullable=True, default=None)
cost_model = relationship(
"CostModel",
foreign_keys=[cost_model_id],
)

@hybrid_method
def get_meta(self, slug):
if not self.plugin_metadata:
Expand Down Expand Up @@ -92,6 +99,7 @@ class CaseTypeBase(DispatchBase):
plugin_metadata: List[PluginMetadata] = []
project: Optional[ProjectRead]
visibility: Optional[str] = Field(None, nullable=True)
cost_model: Optional[CostModelRead] = None

@validator("plugin_metadata", pre=True)
def replace_none_with_empty_list(cls, value):
Expand Down
46 changes: 46 additions & 0 deletions src/dispatch/case_cost/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from sqlalchemy import Column, ForeignKey, Integer, Numeric
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relationship
from typing import List, Optional

from dispatch.database.core import Base
from dispatch.case_cost_type.models import CaseCostTypeRead
from dispatch.models import DispatchBase, Pagination, PrimaryKey, ProjectMixin, TimeStampMixin
from dispatch.project.models import ProjectRead


# SQLAlchemy Model
class CaseCost(Base, TimeStampMixin, ProjectMixin):
# columns
id = Column(Integer, primary_key=True)
amount = Column(Numeric(precision=10, scale=2), nullable=True)

# relationships
case_cost_type = relationship("CaseCostType", backref="case_cost")
case_cost_type_id = Column(Integer, ForeignKey("case_cost_type.id"))
case_id = Column(Integer, ForeignKey("case.id", ondelete="CASCADE"))
search_vector = association_proxy("case_cost_type", "search_vector")


# Pydantic Models
class CaseCostBase(DispatchBase):
amount: float = 0


class CaseCostCreate(CaseCostBase):
case_cost_type: CaseCostTypeRead
project: ProjectRead


class CaseCostUpdate(CaseCostBase):
id: Optional[PrimaryKey] = None
case_cost_type: CaseCostTypeRead


class CaseCostRead(CaseCostBase):
id: PrimaryKey
case_cost_type: CaseCostTypeRead


class CaseCostPagination(Pagination):
items: List[CaseCostRead] = []
66 changes: 66 additions & 0 deletions src/dispatch/case_cost_type/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from datetime import datetime
from typing import List, Optional
from pydantic import Field

from sqlalchemy import Column, Integer, String, Boolean
from sqlalchemy.event import listen

from sqlalchemy_utils import TSVectorType, JSONType

from dispatch.database.core import Base, ensure_unique_default_per_project
from dispatch.models import (
DispatchBase,
NameStr,
ProjectMixin,
TimeStampMixin,
Pagination,
PrimaryKey,
)
from dispatch.project.models import ProjectRead


# SQLAlchemy Model
class CaseCostType(Base, TimeStampMixin, ProjectMixin):
# columns
id = Column(Integer, primary_key=True)
name = Column(String)
description = Column(String)
category = Column(String)
details = Column(JSONType, nullable=True)
default = Column(Boolean, default=False)
editable = Column(Boolean, default=True)

# full text search capabilities
search_vector = Column(
TSVectorType("name", "description", weights={"name": "A", "description": "B"})
)


listen(CaseCostType.default, "set", ensure_unique_default_per_project)


# Pydantic Models
class CaseCostTypeBase(DispatchBase):
name: NameStr
description: Optional[str] = Field(None, nullable=True)
category: Optional[str] = Field(None, nullable=True)
details: Optional[dict] = {}
created_at: Optional[datetime]
default: Optional[bool]
editable: Optional[bool]


class CaseCostTypeCreate(CaseCostTypeBase):
project: ProjectRead


class CaseCostTypeUpdate(CaseCostTypeBase):
id: PrimaryKey = None


class CaseCostTypeRead(CaseCostTypeBase):
id: PrimaryKey


class CaseCostTypePagination(Pagination):
items: List[CaseCostTypeRead] = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""empty message
Revision ID: 15a8d3228123
Revises: 4286dcce0a2d
Create Date: 2024-06-28 11:19:27.227089
"""

from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils

# revision identifiers, used by Alembic.
revision = "15a8d3228123"
down_revision = "4286dcce0a2d"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"case_cost_type",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=True),
sa.Column("description", sa.String(), nullable=True),
sa.Column("category", sa.String(), nullable=True),
sa.Column("details", sqlalchemy_utils.types.json.JSONType(), nullable=True),
sa.Column("default", sa.Boolean(), nullable=True),
sa.Column("editable", sa.Boolean(), nullable=True),
sa.Column("search_vector", sqlalchemy_utils.types.ts_vector.TSVectorType(), nullable=True),
sa.Column("project_id", sa.Integer(), nullable=True),
sa.Column("updated_at", sa.DateTime(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(["project_id"], ["project.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"case_cost_type_search_vector_idx",
"case_cost_type",
["search_vector"],
unique=False,
postgresql_using="gin",
)
op.create_table(
"case_cost",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("amount", sa.Numeric(precision=10, scale=2), nullable=True),
sa.Column("case_cost_type_id", sa.Integer(), nullable=True),
sa.Column("case_id", sa.Integer(), nullable=True),
sa.Column("project_id", sa.Integer(), nullable=True),
sa.Column("updated_at", sa.DateTime(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["case_cost_type_id"],
["case_cost_type.id"],
),
sa.ForeignKeyConstraint(["case_id"], ["case.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["project_id"], ["project.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.add_column("case_type", sa.Column("cost_model_id", sa.Integer(), nullable=True))
op.create_foreign_key(None, "case_type", "cost_model", ["cost_model_id"], ["id"])
op.add_column("participant_activity", sa.Column("case_id", sa.Integer(), nullable=True))
op.create_foreign_key(None, "participant_activity", "case", ["case_id"], ["id"])
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "participant_activity", type_="foreignkey")
op.drop_column("participant_activity", "case_id")
op.drop_constraint(None, "case_type", type_="foreignkey")
op.drop_column("case_type", "cost_model_id")
op.drop_table("case_cost")
op.drop_index(
"case_cost_type_search_vector_idx", table_name="case_cost_type", postgresql_using="gin"
)
op.drop_table("case_cost_type")
# ### end Alembic commands ###
12 changes: 8 additions & 4 deletions src/dispatch/participant_activity/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from datetime import datetime
from sqlalchemy import Column, Integer, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from typing import Optional

from dispatch.case.models import CaseRead
from dispatch.database.core import Base
from dispatch.incident.models import IncidentRead
from dispatch.models import DispatchBase, PrimaryKey
Expand All @@ -26,14 +26,18 @@ class ParticipantActivity(Base):
incident_id = Column(Integer, ForeignKey("incident.id"))
incident = relationship("Incident", foreign_keys=[incident_id])

case_id = Column(Integer, ForeignKey("case.id"))
case = relationship("Case", foreign_keys=[case_id])


# Pydantic Models
class ParticipantActivityBase(DispatchBase):
plugin_event: PluginEventRead
started_at: Optional[datetime] = None
ended_at: Optional[datetime] = None
started_at: datetime | None
ended_at: datetime | None
participant: ParticipantRead
incident: IncidentRead
incident: IncidentRead | None
case: CaseRead | None


class ParticipantActivityRead(ParticipantActivityBase):
Expand Down
17 changes: 17 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from .database import Session
from .factories import (
CaseFactory,
CaseCostFactory,
CaseCostTypeFactory,
CasePriorityFactory,
CaseSeverityFactory,
CaseTypeFactory,
Expand Down Expand Up @@ -548,6 +550,21 @@ def feedbacks(session):
return [FeedbackFactory(), FeedbackFactory()]


@pytest.fixture
def case_cost(session):
return CaseCostFactory()


@pytest.fixture
def case_costs(session):
return [CaseCostFactory(), CaseCostFactory()]


@pytest.fixture
def case_cost_type(session):
return CaseCostTypeFactory()


@pytest.fixture
def incident_cost(session):
return IncidentCostFactory()
Expand Down
Loading

0 comments on commit d299946

Please sign in to comment.