Skip to content

Commit cbb4b1f

Browse files
committed
fix(reports): ensure incident_reports table exists on init
- Add incident_reports table creation in _init_tables - Handle UndefinedTableError gracefully in get_saved_report - Remove duplicate table creation in generate_and_save
1 parent e86dd81 commit cbb4b1f

3 files changed

Lines changed: 39 additions & 31 deletions

File tree

326 KB
Loading
116 KB
Loading

backend/src/oncall_agent/services/incident_service.py

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from src.oncall_agent.api.schemas import (
1010
ActionType,
1111
AIAnalysis,
12-
IncidentAction,
1312
Incident,
13+
IncidentAction,
1414
IncidentStatus,
1515
Severity,
1616
)
@@ -44,7 +44,7 @@ async def get_pool() -> asyncpg.Pool:
4444
if not postgres_url:
4545
raise RuntimeError("No PostgreSQL connection URL configured (POSTGRES_URL, DATABASE_URL, or NEON_DATABASE_URL)")
4646

47-
logger.info(f"Connecting to PostgreSQL database...")
47+
logger.info("Connecting to PostgreSQL database...")
4848
_pool = await asyncpg.create_pool(
4949
postgres_url,
5050
min_size=2,
@@ -105,6 +105,16 @@ async def _init_tables() -> None:
105105
)
106106
""")
107107

108+
# Create incident_reports table
109+
await conn.execute("""
110+
CREATE TABLE IF NOT EXISTS incident_reports (
111+
incident_id TEXT PRIMARY KEY REFERENCES incidents(id) ON DELETE CASCADE,
112+
json_report JSONB NOT NULL,
113+
markdown_report TEXT NOT NULL,
114+
generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
115+
)
116+
""")
117+
108118
# Create indexes for common queries
109119
await conn.execute("""
110120
CREATE INDEX IF NOT EXISTS idx_incidents_status ON incidents(status)
@@ -444,16 +454,6 @@ async def generate_and_save(incident_id: str) -> dict[str, Any]:
444454
now = datetime.now(UTC)
445455

446456
async with pool.acquire() as conn:
447-
# Create reports table if not exists
448-
await conn.execute("""
449-
CREATE TABLE IF NOT EXISTS incident_reports (
450-
incident_id TEXT PRIMARY KEY REFERENCES incidents(id) ON DELETE CASCADE,
451-
json_report JSONB NOT NULL,
452-
markdown_report TEXT NOT NULL,
453-
generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
454-
)
455-
""")
456-
457457
await conn.execute(
458458
"""
459459
INSERT INTO incident_reports (incident_id, json_report, markdown_report, generated_at)
@@ -563,13 +563,13 @@ def _generate_markdown_report(incident: Incident, analysis: dict) -> str:
563563
md_lines.append(ai_data['analysis'])
564564
else:
565565
if ai_data.get('summary'):
566-
md_lines.extend([f"### Summary", "", ai_data['summary'], ""])
566+
md_lines.extend(["### Summary", "", ai_data['summary'], ""])
567567
if ai_data.get('root_cause'):
568-
md_lines.extend([f"### Root Cause", "", ai_data['root_cause'], ""])
568+
md_lines.extend(["### Root Cause", "", ai_data['root_cause'], ""])
569569
if ai_data.get('impact_assessment'):
570-
md_lines.extend([f"### Impact Assessment", "", ai_data['impact_assessment'], ""])
570+
md_lines.extend(["### Impact Assessment", "", ai_data['impact_assessment'], ""])
571571
if ai_data.get('recommended_actions'):
572-
md_lines.extend([f"### Recommended Actions", ""])
572+
md_lines.extend(["### Recommended Actions", ""])
573573
for i, action in enumerate(ai_data['recommended_actions'], 1):
574574
if isinstance(action, dict):
575575
md_lines.append(f"{i}. **{action.get('action', 'Unknown')}**: {action.get('reason', '')}")
@@ -617,23 +617,31 @@ def _generate_markdown_report(incident: Incident, analysis: dict) -> str:
617617
@staticmethod
618618
async def get_saved_report(incident_id: str) -> dict[str, Any] | None:
619619
"""Get saved reports for an incident."""
620-
pool = await get_pool()
620+
try:
621+
pool = await get_pool()
621622

622-
async with pool.acquire() as conn:
623-
row = await conn.fetchrow(
624-
"SELECT json_report, markdown_report, generated_at FROM incident_reports WHERE incident_id = $1",
625-
incident_id
626-
)
623+
async with pool.acquire() as conn:
624+
row = await conn.fetchrow(
625+
"SELECT json_report, markdown_report, generated_at FROM incident_reports WHERE incident_id = $1",
626+
incident_id
627+
)
627628

628-
if not row:
629+
if not row:
630+
return None
631+
632+
json_data = row['json_report']
633+
return {
634+
"json_report": json_data if isinstance(json_data, dict) else json.loads(json_data),
635+
"markdown_report": row['markdown_report'],
636+
"generated_at": row['generated_at'].isoformat() if row['generated_at'] else None,
637+
}
638+
except asyncpg.exceptions.UndefinedTableError:
639+
# Table doesn't exist yet - return None and let caller generate on-the-fly
640+
logger.warning(f"incident_reports table does not exist, returning None for incident {incident_id}")
641+
return None
642+
except Exception as e:
643+
logger.error(f"Error getting saved report for {incident_id}: {e}")
629644
return None
630-
631-
json_data = row['json_report']
632-
return {
633-
"json_report": json_data if isinstance(json_data, dict) else json.loads(json_data),
634-
"markdown_report": row['markdown_report'],
635-
"generated_at": row['generated_at'].isoformat() if row['generated_at'] else None,
636-
}
637645

638646

639647
async def check_database_health() -> dict[str, Any]:
@@ -642,7 +650,7 @@ async def check_database_health() -> dict[str, Any]:
642650
pool = await get_pool()
643651
async with pool.acquire() as conn:
644652
# Test query
645-
result = await conn.fetchval("SELECT 1")
653+
await conn.fetchval("SELECT 1")
646654

647655
# Get some stats
648656
incident_count = await conn.fetchval("SELECT COUNT(*) FROM incidents")

0 commit comments

Comments
 (0)