@cryptotaxi247 / CoPilot / commits / e43c67b0

562 sla to alerts (#563)

* feat: update alert status handling to manage time_closed for CLOSED and OPEN states * feat: add case_closed_time column and update handling in case status * feat: add case_closed_time and time_closed to reports and templates * Implement feature X to enhance user experience and optimize performance * precommit-fixes * feat: update CURRENT_VERSION to 0.1.12

taylor_socfortress committed Dec 16, 2025 at 09:53 UTC e43c67b0346fe26554f884703a093ebb4e7f9e5d
9 files changed +57 -1
backend/alembic/versions/ea1e223c3d62_add_case_closed_time_column.py new
+31
@@ -0,0 +1,31 @@
1 +"""Add case closed time column
2 +
3 +Revision ID: ea1e223c3d62
4 +Revises: e74210501112
5 +Create Date: 2025-12-16 09:29:33.243448
6 +
7 +"""
8 +from typing import Sequence
9 +from typing import Union
10 +
11 +import sqlalchemy as sa
12 +
13 +from alembic import op
14 +
15 +# revision identifiers, used by Alembic.
16 +revision: str = "ea1e223c3d62"
17 +down_revision: Union[str, None] = "e74210501112"
18 +branch_labels: Union[str, Sequence[str], None] = None
19 +depends_on: Union[str, Sequence[str], None] = None
20 +
21 +
22 +def upgrade() -> None:
23 + # ### commands auto generated by Alembic - please adjust! ###
24 + op.add_column("incident_management_case", sa.Column("case_closed_time", sa.DateTime(), nullable=True))
25 + # ### end Alembic commands ###
26 +
27 +
28 +def downgrade() -> None:
29 + # ### commands auto generated by Alembic - please adjust! ###
30 + op.drop_column("incident_management_case", "case_closed_time")
31 + # ### end Alembic commands ###
backend/app/incidents/models.py
+1
@@ -164,6 +164,7 @@ class Case(SQLModel, table=True):
164 case_description: str = Field(sa_column=Text)
165 case_creation_time: datetime = Field(default_factory=datetime.utcnow)
166 case_status: str = Field(max_length=50, nullable=False)
167 + case_closed_time: Optional[datetime] = Field(default=None, nullable=True)
168 assigned_to: Optional[str] = Field(max_length=50, nullable=True)
169 customer_code: Optional[str] = Field(max_length=50, nullable=True)
170 notification_invoked_number: Optional[int] = Field(default=0, nullable=True)
backend/app/incidents/routes/incident_report.py
+2
@@ -44,6 +44,7 @@ FIELDNAMES = [
44 "Case Description",
45 "Case Creation Time",
46 "Case Status",
47 + "Case Closed Time",
48 "Case Assigned To",
49 "Case Customer Code",
50 "Alert ID",
@@ -119,6 +120,7 @@ def serialize_case_alert_to_row(case: Case, alert: Alert) -> Dict[str, Any]:
120 "Case Description": case.case_description,
121 "Case Creation Time": case.case_creation_time.strftime("%Y-%m-%d %H:%M:%S"),
122 "Case Status": case.case_status,
123 + "Case Closed Time": case.case_closed_time.strftime("%Y-%m-%d %H:%M:%S") if case.case_closed_time else "",
124 "Case Assigned To": case.assigned_to or "",
125 "Case Customer Code": case.customer_code or "",
126 "Alert ID": alert.id,
backend/app/incidents/services/db_operations.py
+16
@@ -792,6 +792,14 @@ async def update_alert_status(update_alert_status: UpdateAlertStatus, db: AsyncS
792 if not alert:
793 raise HTTPException(status_code=404, detail="Alert not found")
794 alert.status = update_alert_status.status
795 +
796 + # Set time_closed if status is CLOSED
797 + if update_alert_status.status == "CLOSED":
798 + alert.time_closed = datetime.utcnow()
799 + # Reset time_closed if status is OPEN
800 + elif update_alert_status.status == "OPEN":
801 + alert.time_closed = None
802 +
803 await db.commit()
804 return alert
805
@@ -802,6 +810,14 @@ async def update_case_status(update_case_status: UpdateCaseStatus, db: AsyncSess
810 if not case:
811 raise HTTPException(status_code=404, detail="Case not found")
812 case.case_status = update_case_status.status
813 +
814 + # Set case_closed_time if status is CLOSED
815 + if update_case_status.status == "CLOSED":
816 + case.case_closed_time = datetime.utcnow()
817 + # Reset case_closed_time if status is OPEN
818 + elif update_case_status.status == "OPEN":
819 + case.case_closed_time = None
820 +
821 await db.commit()
822 return case
823
backend/app/incidents/services/reports.py
+2
@@ -22,12 +22,14 @@ def create_case_context(case) -> Dict[str, Dict[str, str]]:
22 "description": case.case_description,
23 "assigned_to": case.assigned_to,
24 "case_creation_time": case.case_creation_time,
25 + "case_closed_time": case.case_closed_time,
26 "id": case.id,
27 "alerts": [
28 {
29 "alert_name": alert.alert.alert_name,
30 "alert_description": alert.alert.alert_description,
31 "status": alert.alert.status,
32 + "time_closed": alert.alert.time_closed,
33 "tags": [tag.tag.tag for tag in alert.alert.tags],
34 "assets": [
35 {
backend/app/incidents/services/reports_pdf.py
+2
@@ -28,12 +28,14 @@ def create_case_context_pdf(case) -> Dict[str, Dict[str, str]]:
28 "description": case.case_description,
29 "assigned_to": case.assigned_to,
30 "case_creation_time": case.case_creation_time,
31 + "case_closed_time": case.case_closed_time,
32 "id": case.id,
33 "alerts": [
34 {
35 "alert_name": alert.alert.alert_name,
36 "alert_description": alert.alert.alert_description,
37 "status": alert.alert.status,
38 + "time_closed": alert.alert.time_closed,
39 "tags": [tag.tag.tag for tag in alert.alert.tags],
40 "assets": [
41 {
backend/app/incidents/templates/case_report_jinja_template.docx
Binary files a/backend/app/incidents/templates/case_report_jinja_template.docx and b/backend/app/incidents/templates/case_report_jinja_template.docx differ
backend/app/incidents/templates/case_report_jinja_template.html
+2
@@ -15,6 +15,7 @@
15 <p><strong>Description:</strong> {{ case.description }}</p>
16 <p><strong>Assigned To:</strong> {{ case.assigned_to }}</p>
17 <p><strong>Case Creation Time:</strong> {{ case.case_creation_time }}</p>
18 + <p><strong>Case Closed Time:</strong> {{ case.case_closed_time if case.case_closed_time else 'N/A' }}</p>
19 <p><strong>Case ID:</strong> {{ case.id }}</p>
20 </div>
21
@@ -24,6 +25,7 @@
25 <p><strong>Alert Name:</strong> {{ alert.alert_name }}</p>
26 <p><strong>Description:</strong> {{ alert.alert_description }}</p>
27 <p><strong>Status:</strong> {{ alert.status }}</p>
28 + <p><strong>Time Closed:</strong> {{ alert.time_closed if alert.time_closed else 'N/A' }}</p>
29 <p><strong>Tags:</strong> {{ alert.tags | join(', ') }}</p>
30
31 <h3>Assets:</h3>
backend/app/version/services/version.py
+1 -1
@@ -7,7 +7,7 @@ from loguru import logger
7 from packaging.version import Version
8
9 # Current version - update this with each release
10 -CURRENT_VERSION = "0.1.11"
10 +CURRENT_VERSION = "0.1.12"
11 VERSION_CHECK_URL = "https://api.github.com/repos/socfortress/CoPilot/releases/latest"
12
13