Spaces:
Sleeping
Sleeping
File size: 13,025 Bytes
6ba374a be1f66b 6ba374a be1f66b 00fabe1 be1f66b 00fabe1 6ba374a 00fabe1 6ba374a be1f66b 00fabe1 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b 6ba374a be1f66b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | import os
import json
import sqlite3
from typing import Tuple, List, Dict, Any, Optional
try:
import psycopg
from psycopg.rows import dict_row
except ImportError:
psycopg = None
dict_row = None
SQLITE_DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "tickets_fallback.db")
SAMPLE_TICKETS = [
("Login Page Freezes After Multiple Failed Attempts", "Users are reporting that after 3 failed login attempts, the login page becomes unresponsive and requires a refresh. No specific error message is displayed.", "samuel.green@example.com", "P0 - Critical", "Open"),
("Dashboard Sales Widget Intermittent Data Loading Failure", "The \"Sales Overview\" widget on the main dashboard intermittently shows a loading spinner but no data. Primarily affects Chrome browser users.", "maria.rodriguez@example.com", "P1 - High", "In Progress"),
("Broken Link in Footer - Privacy Policy", "The \"Privacy Policy\" hyperlink located in the website footer leads to a 404 \"Page Not Found\" error.", "maria.rodriguez@example.com", "P3 - Low", "Resolved"),
("UI Misalignment on Mobile Landscape View (iOS)", "On specific iOS devices (e.g., iPhone 14 models), the top navigation bar shifts downwards when the device is viewed in landscape orientation, obscuring content.", "maria.rodriguez@example.com", "P2 - Medium", "In Progress"),
("Critical XZ Utils Backdoor Detected in Core Dependency (CVE-2024-3094)", "Urgent: A sophisticated supply chain compromise (CVE-2024-3094) has been identified in XZ Utils versions 5.6.0 and 5.6.1. This malicious code potentially allows unauthorized remote SSH access by modifying liblzma. Immediate investigation and action required for affected Linux/Unix systems and services relying on XZ Utils.", "frank.white@example.com", "P0 - Critical", "Open"),
("Database Connection Timeouts During Peak Usage", "The application is experiencing frequent database connection timeouts, particularly during peak hours (10 AM - 12 PM EDT), affecting all users and causing service interruptions.", "frank.white@example.com", "P1 - High", "Open"),
("Export to PDF Truncates Long Text Fields in Reports", "When generating PDF exports of reports containing extensive text fields, the text is abruptly cut off at the end of the page instead of wrapping or continuing to the next page.", "samuel.green@example.com", "P1 - High", "Open"),
("Search Filter \"Date Range\" Not Applying Correctly", "The \"Date Range\" filter on the search results page does not filter records accurately; results outside the specified date range are still displayed.", "samuel.green@example.com", "P2 - Medium", "Resolved"),
("Typo in Error Message: \"Unathorized Access\"", "The error message displayed when a user attempts an unauthorized action reads \"Unathorized Access\" instead of \"Unauthorized Access.\"", "maria.rodriguez@example.com", "P3 - Low", "Resolved"),
("Intermittent File Upload Failures for Large Files", "Users are intermittently reporting that file uploads fail without a clear error message or explanation, especially for files exceeding 10MB in size.", "frank.white@example.com", "P1 - High", "Open")
]
def _init_sqlite_db():
"""Ensure the local fallback SQLite database has the tickets table and initial sample tickets."""
try:
conn = sqlite3.connect(SQLITE_DB_PATH)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS tickets (
ticket_id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
assignee TEXT,
priority TEXT,
status TEXT DEFAULT 'Open',
creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
cur.execute("SELECT COUNT(*) FROM tickets")
count = cur.fetchone()[0]
if count == 0:
cur.executemany(
"INSERT INTO tickets (title, description, assignee, priority, status) VALUES (?, ?, ?, ?, ?)",
SAMPLE_TICKETS
)
conn.commit()
conn.close()
except Exception as e:
print(f"Fallback SQLite init error: {e}")
# Initialize local fallback database
_init_sqlite_db()
def _get_db_conn():
if not psycopg:
raise Exception("psycopg library not available")
# Direct connection string (common in Supabase / Neon / Heroku / HF)
db_url = os.getenv("DATABASE_URL") or os.getenv("POSTGRES_URL") or os.getenv("SUPABASE_DB_URL")
if db_url:
return psycopg.connect(db_url, row_factory=dict_row, connect_timeout=4)
db_host = os.getenv("DB_HOST")
db_port = int(os.getenv("DB_PORT", "5432"))
db_name = os.getenv("DB_NAME", "postgres")
db_user = os.getenv("DB_USER", "postgres")
db_pass = os.getenv("DB_PASS") or os.getenv("DB_PASSWORD")
if not db_host:
raise Exception("Database configuration missing: DB_HOST (or DATABASE_URL) environment variable is not set.")
# Supabase / cloud DBs need SSL, local dev usually doesn't
sslmode = os.getenv("DB_SSLMODE")
if not sslmode:
sslmode = "disable" if db_host in ("127.0.0.1", "localhost") else "require"
conninfo = f"host={db_host} port={db_port} dbname={db_name} user={db_user} password={db_pass} sslmode={sslmode} connect_timeout=4"
return psycopg.connect(
conninfo,
row_factory=dict_row
)
def _execute_sqlite_query(query: str, params: tuple = ()) -> List[Dict[str, Any]]:
_init_sqlite_db()
conn = sqlite3.connect(SQLITE_DB_PATH)
conn.row_factory = sqlite3.Row
cur = conn.cursor()
sqlite_query = query.replace("%s", "?").replace("ILIKE", "LIKE")
cur.execute(sqlite_query, params)
if sqlite_query.strip().upper().startswith("SELECT") or "RETURNING" in sqlite_query.upper():
rows = [dict(r) for r in cur.fetchall()]
conn.commit()
conn.close()
return rows
conn.commit()
last_id = cur.lastrowid
conn.close()
if "INSERT" in sqlite_query.upper():
return [{"ticket_id": last_id}]
return []
def _execute_query(query: str, params: tuple = ()) -> Tuple[List[Dict[str, Any]], bool, Optional[str]]:
"""Execute a query against PostgreSQL first, falling back to local SQLite if unavailable."""
# Try PostgreSQL first
try:
with _get_db_conn() as conn:
with conn.cursor() as cur:
cur.execute(query, params)
if query.strip().upper().startswith("SELECT") or "RETURNING" in query.upper():
return cur.fetchall(), False, None
conn.commit()
return [], False, None
except Exception as pg_err:
print(f"[Database] PostgreSQL unavailable ({pg_err}). Falling back to local SQLite database.")
try:
sqlite_rows = _execute_sqlite_query(query, params)
return sqlite_rows, True, str(pg_err)
except Exception as sqlite_err:
raise Exception(f"PostgreSQL error: {pg_err} | SQLite error: {sqlite_err}")
def get_tickets_by_status(status: str) -> dict:
try:
tickets, is_fallback, pg_err = _execute_query(
"SELECT * FROM tickets WHERE status ILIKE %s ORDER BY creation_time DESC",
(status,)
)
res = {"success": True, "tickets": tickets, "count": len(tickets)}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "tickets": []}
def get_tickets_by_priority(priority: str) -> dict:
try:
tickets, is_fallback, pg_err = _execute_query(
"SELECT * FROM tickets WHERE priority ILIKE %s ORDER BY creation_time DESC",
(priority,)
)
res = {"success": True, "tickets": tickets, "count": len(tickets)}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "tickets": []}
def get_ticket_by_id(ticket_id: str) -> dict:
try:
tickets, is_fallback, pg_err = _execute_query(
"SELECT * FROM tickets WHERE ticket_id = %s",
(int(ticket_id),)
)
ticket = tickets[0] if tickets else None
res = {"success": True, "ticket": ticket}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "ticket": None}
def get_tickets_by_assignee(assignee: str) -> dict:
try:
tickets, is_fallback, pg_err = _execute_query(
"SELECT * FROM tickets WHERE assignee ILIKE %s ORDER BY creation_time DESC",
(f"%{assignee}%",)
)
res = {"success": True, "tickets": tickets, "count": len(tickets)}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "tickets": []}
def db_create_ticket(title: str, description: str, assignee: str = "", priority: str = "P3 - Low", status: str = "Open") -> dict:
try:
tickets, is_fallback, pg_err = _execute_query(
"INSERT INTO tickets (title, description, assignee, priority, status) VALUES (%s, %s, %s, %s, %s) RETURNING ticket_id",
(title, description, assignee, priority, status)
)
ticket_id = tickets[0]['ticket_id'] if tickets else None
res = {"success": True, "ticket_id": str(ticket_id), "message": "Ticket created successfully"}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "ticket_id": None}
def update_ticket_status(ticket_id: str, status: str) -> dict:
try:
_, is_fallback, pg_err = _execute_query(
"UPDATE tickets SET status = %s, updated_time = CURRENT_TIMESTAMP WHERE ticket_id = %s",
(status, int(ticket_id))
)
res = {"success": True, "message": f"Ticket {ticket_id} status updated to {status}"}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "success": False}
def update_ticket_priority(ticket_id: str, priority: str) -> dict:
try:
_, is_fallback, pg_err = _execute_query(
"UPDATE tickets SET priority = %s, updated_time = CURRENT_TIMESTAMP WHERE ticket_id = %s",
(priority, int(ticket_id))
)
res = {"success": True, "message": f"Ticket {ticket_id} priority updated to {priority}"}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "success": False}
def get_tickets_by_date_range(start_date: str, end_date: str, date_field: str = "creation_time") -> dict:
try:
query = f"SELECT * FROM tickets WHERE {date_field} >= %s AND {date_field} <= %s ORDER BY {date_field} DESC"
tickets, is_fallback, pg_err = _execute_query(query, (start_date, end_date))
res = {"success": True, "tickets": tickets, "count": len(tickets)}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
return {"error": str(e), "tickets": []}
def update_ticket_embedding(ticket_id: str, embedding: str):
try:
_execute_query("UPDATE tickets SET embedding = %s::vector WHERE ticket_id = %s", (embedding, int(ticket_id)))
except Exception as e:
print(f"Failed to update embedding: {e}")
def search_tickets_vector(embedding_vector: str, limit: int = 5) -> dict:
try:
tickets, is_fallback, pg_err = _execute_query(
"SELECT ticket_id, title, description, status, priority, assignee, 1 - (embedding <=> %s::vector) as similarity FROM tickets WHERE embedding IS NOT NULL ORDER BY embedding <=> %s::vector LIMIT %s",
(embedding_vector, embedding_vector, limit)
)
res = {"success": True, "tickets": tickets, "count": len(tickets)}
if is_fallback:
res["is_fallback"] = True
res["fallback_reason"] = pg_err
return res
except Exception as e:
try:
sqlite_tickets = _execute_sqlite_query("SELECT ticket_id, title, description, status, priority, assignee, 0.9 as similarity FROM tickets LIMIT ?", (limit,))
return {"success": True, "tickets": sqlite_tickets, "count": len(sqlite_tickets), "is_fallback": True}
except Exception:
return {"error": str(e), "tickets": []}
|