from flask import jsonify, request import logging from datetime import datetime from models.incident import Incident from models.workflow import Workflow from models.log import Log from utils.celery_tasks import process_incident_forms from models.user import User from models.department import Department from utils.pdf_utils import fill_markdown_form, extract_required_data, save_filled_form from db import get_gridfs from bson.objectid import ObjectId import os import openai # Configure logging logger = logging.getLogger(__name__) def get_incident(current_user, incident_id): """Get incident by ID""" try: # Find incident by ID incident = Incident.find_by_id(incident_id) if not incident: return jsonify({'message': 'Incident not found'}), 404 # Check if user has permission to access this incident # An Admin can access any incident in their department. # A regular user can only access their own incidents. is_admin = current_user.permissions == 'Admin' is_owner = str(incident.user_id) == str(current_user._id) in_department = str(incident.department_id) == str(current_user.department_id) if not in_department: return jsonify({'message': 'Cannot access incidents outside your department'}), 403 if not is_admin and not is_owner: return jsonify({'message': 'You do not have permission to access this incident'}), 403 # Return incident return jsonify({'incident': incident.to_dict()}), 200 except Exception as e: logger.error(f"Error getting incident: {str(e)}") return jsonify({'message': f'Error getting incident: {str(e)}'}), 500 def delete_incident(current_user, incident_id): """Delete incident by ID""" try: # Find incident by ID incident = Incident.find_by_id(incident_id) if not incident: return jsonify({'message': 'Incident not found'}), 404 # Check if user has access to this incident if not current_user.is_admin and str(incident.user_id) != str(current_user._id): return jsonify({'message': 'You do not have permission to delete this incident'}), 403 # Delete incident if incident.delete(): return jsonify({'message': 'Incident deleted successfully'}), 200 else: return jsonify({'message': 'Failed to delete incident'}), 500 except Exception as e: logger.error(f"Error deleting incident: {str(e)}") return jsonify({'message': f'Error deleting incident: {str(e)}'}), 500 def process_incident_sync(current_user, incident_id): """Process an incident synchronously (fill form with LLM)""" try: # Check if OpenAI API key is set api_key = os.environ.get('OPENAI_API_KEY') if not api_key: logger.error("OPENAI_API_KEY environment variable is not set") return jsonify({'message': 'OpenAI API key not configured'}), 500 # Create OpenAI client - removed any proxies parameter client = openai.OpenAI(api_key=api_key) # Find incident by ID incident = Incident.find_by_id(incident_id) if not incident: return jsonify({'message': 'Incident not found'}), 404 # Check if user has permission to process this incident # An Admin can process any incident in their department. # A regular user can only process their own incidents. is_admin = current_user.permissions == 'Admin' is_owner = str(incident.user_id) == str(current_user._id) in_department = str(incident.department_id) == str(current_user.department_id) # Check permissions based on role if not in_department: # Should generally not happen if incidents are fetched correctly, but good failsafe return jsonify({'message': 'Cannot process incidents outside your department'}), 403 if not is_admin and not is_owner: return jsonify({'message': 'You do not have permission to process this incident'}), 403 # Get the workflow for this incident workflow = Workflow.find_by_id(incident.workflow_id) if not workflow: return jsonify({'message': 'Workflow not found for this incident'}), 404 # Check if workflow has a template if not workflow.markdown_template: return jsonify({'message': 'No form template found for this workflow'}), 404 # Extract required data from incident logger.info(f"Extracting required data for incident {incident_id}") required_data = extract_required_data(incident.activity_text, workflow.data_requirements) # Store the extracted data in the incident incident.extracted_data = required_data # Fill markdown form logger.info(f"Filling form for incident {incident_id}") filled_markdown = fill_markdown_form(workflow.markdown_template, required_data) # Update incident status incident.status = "completed" if incident.save(): return jsonify({ 'message': 'Incident processed successfully', 'incident': incident.to_dict(), 'filled_markdown': filled_markdown }), 200 else: return jsonify({'message': 'Failed to update incident'}), 500 except Exception as e: logger.error(f"Error processing incident {incident_id}: {str(e)}") return jsonify({'message': f'Error processing incident: {str(e)}'}), 500 def get_user_incidents(current_user): """Get all incidents for the current user""" try: # Find incidents by user ID incidents = Incident.find_by_user(current_user._id) # Convert incidents to dict incidents_dict = [incident.to_dict() for incident in incidents] return jsonify({'incidents': incidents_dict}), 200 except Exception as e: logger.error(f"Error getting user incidents: {str(e)}") return jsonify({'message': f'Error getting user incidents: {str(e)}'}), 500 def get_department_incidents(current_user): """Get all incidents for the user's department""" incidents = Incident.find_by_department(current_user.department_id) return jsonify({'incidents': [incident.to_dict() for incident in incidents]}), 200 def get_workflow_incidents(current_user, workflow_id): """Get all incidents for a specific workflow""" # Check if workflow exists and belongs to user's department workflow = Workflow.find_by_id(workflow_id) if not workflow: return jsonify({'message': 'Workflow not found'}), 404 if str(workflow.department_id) != str(current_user.department_id): return jsonify({'message': 'Access denied to workflows from other departments'}), 403 incidents = Incident.find_by_workflow(workflow_id) return jsonify({'incidents': [incident.to_dict() for incident in incidents]}), 200 def get_incidents_by_date_range(current_user): """Get incidents by date range""" data = request.get_json() # Check if required fields are present if 'start_date' not in data or 'end_date' not in data: return jsonify({'message': 'Start date and end date are required'}), 400 try: # Parse date strings start_date = datetime.strptime(data['start_date'], '%Y-%m-%d').date() end_date = datetime.strptime(data['end_date'], '%Y-%m-%d').date() # Get incidents by date range incidents = Incident.find_by_date_range(current_user.department_id, start_date, end_date) return jsonify({'incidents': [incident.to_dict() for incident in incidents]}), 200 except ValueError: return jsonify({'message': 'Invalid date format. Please use YYYY-MM-DD'}), 400 except Exception as e: logger.error(f"Error fetching incidents by date range: {str(e)}") return jsonify({'message': f'Error fetching incidents: {str(e)}'}), 500 def reprocess_incident(current_user, incident_id): """Reprocess an incident to generate forms""" # Check if user has admin permissions if current_user.permissions != 'Admin': return jsonify({'message': 'Admin permissions required'}), 403 incident = Incident.find_by_id(incident_id) if not incident: return jsonify({'message': 'Incident not found'}), 404 # Check if user has access to this incident if str(incident.department_id) != str(current_user.department_id): return jsonify({'message': 'Access denied to incidents from other departments'}), 403 try: # Update incident status incident.update_status("processing") # Queue incident for processing process_incident_forms.delay(str(incident._id)) return jsonify({ 'message': 'Incident reprocessing started', 'incident': incident.to_dict() }), 200 except Exception as e: logger.error(f"Error reprocessing incident {incident_id}: {str(e)}") incident.update_status("failed") return jsonify({'message': f'Error reprocessing incident: {str(e)}'}), 500 def create_incident_from_activity(current_user): """Create an incident from activity data and workflow ID""" try: data = request.get_json() # Check if required fields are present required_fields = ['activity', 'workflow_id', 'date', 'log_text'] for field in required_fields: if field not in data: return jsonify({'message': f'Missing required field: {field}'}), 400 # Validate date format try: if isinstance(data['date'], str): date = datetime.strptime(data['date'], '%Y-%m-%d').date() else: date = data['date'] except ValueError: return jsonify({'message': 'Invalid date format. Please use YYYY-MM-DD'}), 400 # Check if workflow exists and user has access workflow = Workflow.find_by_id(data['workflow_id']) if not workflow: return jsonify({'message': 'Workflow not found'}), 404 # Check if user has access to this workflow if str(workflow.department_id) != str(current_user.department_id): return jsonify({'message': 'Access denied to workflows from other departments'}), 403 # Create log entry first if log_id is not provided log_id = data.get('log_id') if not log_id: log = Log( user_id=current_user._id, department_id=current_user.department_id, log_date=date, log_text=data['log_text'] ) if log.save(): log_id = log._id # Create the incident incident = Incident( department_id=current_user.department_id, user_id=current_user._id, workflow_id=ObjectId(data['workflow_id']), description=data['activity'].get('activity', 'No description'), date=date, activity_text=data['activity'].get('text', ''), log_id=log_id, status="pending" ) if incident.save(): # Add incident to log if log exists if log_id: log = Log.find_by_id(log_id) if log: log.add_incident(incident._id) return jsonify({ 'message': 'Incident created successfully', 'incident': incident.to_dict() }), 201 else: return jsonify({'message': 'Failed to create incident'}), 500 except Exception as e: logger.error(f"Error creating incident from activity: {str(e)}") return jsonify({'message': f'Error creating incident: {str(e)}'}), 500