Refactor backend and add registry build workflow
Build and publish Docker image / build-and-publish (push) Successful in 2m45s
Build and publish Docker image / build-and-publish (push) Successful in 2m45s
This commit is contained in:
commit
c177d7981d
43 files changed
+18124
No files matched your search
@@ -0,0 +1,292 @@
|
||||
"""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 {}
|
||||
Reference in new issue
Block a user