293 lines
11 KiB
Python
293 lines
11 KiB
Python
"""Support ticket persistence and message operations."""
|
|
|
|
|
|
from database_service import database_connection, dict_from_row
|
|
from runtime_config import (
|
|
logger,
|
|
)
|
|
from typing import Dict, List, Optional
|
|
import uuid
|
|
|
|
__all__ = ['add_ticket_attachment', 'add_ticket_message', 'create_ticket', 'get_all_tickets', 'get_ticket', 'get_ticket_attachments', 'get_ticket_stats', 'get_user_tickets', 'update_ticket_status']
|
|
|
|
|
|
|
|
def create_ticket(user_id: str, subject: str, description: str, priority: str = 'medium', category: str = 'general') -> Optional[Dict]:
|
|
"""Create a new support ticket"""
|
|
try:
|
|
ticket_id = f"TKT-{str(uuid.uuid4())[:8]}"
|
|
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
INSERT INTO tickets (id, user_id, subject, description, priority, category)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
''', (ticket_id, user_id, subject, description, priority, category))
|
|
conn.commit()
|
|
|
|
cursor.execute('''
|
|
SELECT t.*, u.username as user_username
|
|
FROM tickets t
|
|
JOIN users u ON t.user_id = u.id
|
|
WHERE t.id = ?
|
|
''', (ticket_id,))
|
|
ticket = dict_from_row(cursor.fetchone())
|
|
return ticket
|
|
except Exception as e:
|
|
logger.error(f"Ticket creation failed: {e}")
|
|
return None
|
|
|
|
def get_ticket(ticket_id: str) -> Optional[Dict]:
|
|
"""Get ticket by ID with user information"""
|
|
try:
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
SELECT t.*, u.username as user_username
|
|
FROM tickets t
|
|
JOIN users u ON t.user_id = u.id
|
|
WHERE t.id = ?
|
|
''', (ticket_id,))
|
|
ticket = dict_from_row(cursor.fetchone())
|
|
|
|
if ticket:
|
|
# Get ticket messages
|
|
cursor.execute('''
|
|
SELECT tm.*, u.username as user_username
|
|
FROM ticket_messages tm
|
|
JOIN users u ON tm.user_id = u.id
|
|
WHERE tm.ticket_id = ?
|
|
ORDER BY tm.created_at ASC
|
|
''', (ticket_id,))
|
|
messages = [dict_from_row(row) for row in cursor.fetchall()]
|
|
ticket['messages'] = messages
|
|
|
|
# Get message count
|
|
cursor.execute('SELECT COUNT(*) as count FROM ticket_messages WHERE ticket_id = ?', (ticket_id,))
|
|
ticket['message_count'] = cursor.fetchone()['count']
|
|
|
|
# Get last reply time
|
|
cursor.execute('''
|
|
SELECT MAX(created_at) as last_reply
|
|
FROM ticket_messages
|
|
WHERE ticket_id = ?
|
|
''', (ticket_id,))
|
|
last_reply = cursor.fetchone()['last_reply']
|
|
ticket['last_reply'] = last_reply
|
|
|
|
return ticket
|
|
except Exception as e:
|
|
logger.error(f"Error getting ticket: {e}")
|
|
return None
|
|
|
|
def get_user_tickets(user_id: str, include_closed: bool = False) -> List[Dict]:
|
|
"""Get all tickets for a user"""
|
|
try:
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
|
|
if include_closed:
|
|
cursor.execute('''
|
|
SELECT t.*, u.username as user_username
|
|
FROM tickets t
|
|
JOIN users u ON t.user_id = u.id
|
|
WHERE t.user_id = ?
|
|
ORDER BY t.updated_at DESC
|
|
''', (user_id,))
|
|
else:
|
|
cursor.execute('''
|
|
SELECT t.*, u.username as user_username
|
|
FROM tickets t
|
|
JOIN users u ON t.user_id = u.id
|
|
WHERE t.user_id = ? AND t.status != 'closed'
|
|
ORDER BY t.updated_at DESC
|
|
''', (user_id,))
|
|
|
|
tickets = [dict_from_row(row) for row in cursor.fetchall()]
|
|
|
|
# Add message counts and last reply
|
|
for ticket in tickets:
|
|
cursor.execute('SELECT COUNT(*) as count FROM ticket_messages WHERE ticket_id = ?', (ticket['id'],))
|
|
ticket['message_count'] = cursor.fetchone()['count']
|
|
|
|
cursor.execute('''
|
|
SELECT MAX(created_at) as last_reply
|
|
FROM ticket_messages
|
|
WHERE ticket_id = ?
|
|
''', (ticket['id'],))
|
|
last_reply = cursor.fetchone()['last_reply']
|
|
ticket['last_reply'] = last_reply
|
|
|
|
return tickets
|
|
except Exception as e:
|
|
logger.error(f"Error getting user tickets: {e}")
|
|
return []
|
|
|
|
def get_all_tickets(status_filter: str = None, priority_filter: str = None) -> List[Dict]:
|
|
"""Get all tickets (admin only)"""
|
|
try:
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
|
|
query = '''
|
|
SELECT t.*, u.username as user_username
|
|
FROM tickets t
|
|
JOIN users u ON t.user_id = u.id
|
|
'''
|
|
params = []
|
|
|
|
conditions = []
|
|
if status_filter and status_filter != 'all':
|
|
conditions.append('t.status = ?')
|
|
params.append(status_filter)
|
|
if priority_filter and priority_filter != 'all':
|
|
conditions.append('t.priority = ?')
|
|
params.append(priority_filter)
|
|
|
|
if conditions:
|
|
query += ' WHERE ' + ' AND '.join(conditions)
|
|
|
|
query += ' ORDER BY t.updated_at DESC'
|
|
|
|
cursor.execute(query, params)
|
|
tickets = [dict_from_row(row) for row in cursor.fetchall()]
|
|
|
|
# Add message counts and last reply
|
|
for ticket in tickets:
|
|
cursor.execute('SELECT COUNT(*) as count FROM ticket_messages WHERE ticket_id = ?', (ticket['id'],))
|
|
ticket['message_count'] = cursor.fetchone()['count']
|
|
|
|
cursor.execute('''
|
|
SELECT MAX(created_at) as last_reply
|
|
FROM ticket_messages
|
|
WHERE ticket_id = ?
|
|
''', (ticket['id'],))
|
|
last_reply = cursor.fetchone()['last_reply']
|
|
ticket['last_reply'] = last_reply
|
|
|
|
return tickets
|
|
except Exception as e:
|
|
logger.error(f"Error getting all tickets: {e}")
|
|
return []
|
|
|
|
def add_ticket_message(ticket_id: str, user_id: str, message: str, is_admin: bool = False) -> Optional[Dict]:
|
|
"""Add a message to a ticket"""
|
|
try:
|
|
message_id = f"MSG-{str(uuid.uuid4())[:8]}"
|
|
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
|
|
# Add message
|
|
cursor.execute('''
|
|
INSERT INTO ticket_messages (id, ticket_id, user_id, message, is_admin)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
''', (message_id, ticket_id, user_id, message, is_admin))
|
|
|
|
# Update ticket timestamp
|
|
cursor.execute('''
|
|
UPDATE tickets
|
|
SET updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
''', (ticket_id,))
|
|
|
|
conn.commit()
|
|
|
|
# Get the created message with user info
|
|
cursor.execute('''
|
|
SELECT tm.*, u.username as user_username
|
|
FROM ticket_messages tm
|
|
JOIN users u ON tm.user_id = u.id
|
|
WHERE tm.id = ?
|
|
''', (message_id,))
|
|
message_data = dict_from_row(cursor.fetchone())
|
|
|
|
return message_data
|
|
except Exception as e:
|
|
logger.error(f"Error adding ticket message: {e}")
|
|
return None
|
|
|
|
def update_ticket_status(ticket_id: str, status: str, assigned_admin_id: str = None) -> bool:
|
|
"""Update ticket status"""
|
|
try:
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
|
|
if assigned_admin_id:
|
|
cursor.execute('''
|
|
UPDATE tickets
|
|
SET status = ?, assigned_admin_id = ?, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
''', (status, assigned_admin_id, ticket_id))
|
|
else:
|
|
cursor.execute('''
|
|
UPDATE tickets
|
|
SET status = ?, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
''', (status, ticket_id))
|
|
|
|
conn.commit()
|
|
return cursor.rowcount > 0
|
|
except Exception as e:
|
|
logger.error(f"Error updating ticket status: {e}")
|
|
return False
|
|
|
|
def add_ticket_attachment(ticket_id: str, message_id: str, filename: str, file_data: bytes,
|
|
mime_type: str, uploaded_by: str) -> Optional[Dict]:
|
|
"""Add attachment to ticket"""
|
|
try:
|
|
attachment_id = f"ATT-{str(uuid.uuid4())[:8]}"
|
|
file_size = len(file_data)
|
|
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
INSERT INTO ticket_attachments (id, ticket_id, message_id, filename, file_data, mime_type, file_size, uploaded_by)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
''', (attachment_id, ticket_id, message_id, filename, file_data, mime_type, file_size, uploaded_by))
|
|
conn.commit()
|
|
|
|
cursor.execute('SELECT * FROM ticket_attachments WHERE id = ?', (attachment_id,))
|
|
return dict_from_row(cursor.fetchone())
|
|
except Exception as e:
|
|
logger.error(f"Error adding ticket attachment: {e}")
|
|
return None
|
|
|
|
def get_ticket_attachments(ticket_id: str) -> List[Dict]:
|
|
"""Get all attachments for a ticket"""
|
|
try:
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
SELECT * FROM ticket_attachments
|
|
WHERE ticket_id = ?
|
|
ORDER BY created_at DESC
|
|
''', (ticket_id,))
|
|
return [dict_from_row(row) for row in cursor.fetchall()]
|
|
except Exception as e:
|
|
logger.error(f"Error getting ticket attachments: {e}")
|
|
return []
|
|
|
|
def get_ticket_stats() -> Dict:
|
|
"""Get ticket statistics"""
|
|
try:
|
|
with database_connection() as conn:
|
|
cursor = conn.cursor()
|
|
|
|
stats = {}
|
|
|
|
# Total tickets
|
|
cursor.execute('SELECT COUNT(*) as count FROM tickets')
|
|
stats['total'] = cursor.fetchone()['count']
|
|
|
|
# Tickets by status
|
|
cursor.execute('SELECT status, COUNT(*) as count FROM tickets GROUP BY status')
|
|
status_counts = cursor.fetchall()
|
|
for row in status_counts:
|
|
stats[row['status']] = row['count']
|
|
|
|
return stats
|
|
except Exception as e:
|
|
logger.error(f"Error getting ticket stats: {e}")
|
|
return {}
|