import os from flask import Blueprint, request, jsonify, current_app from werkzeug.utils import secure_filename from src.services.sop_generator import (SopPersonalAssessment,SopGeneratorExecutive) from src.services.sop_document_parser import DocumentParser from src.utils.utils import delete_all_files_in_directory from src.utils.document_loader import load_document import json # Initialize the Blueprint sops_bp = Blueprint('sops', __name__) # Initialize SopGenerator ALLOWED_EXTENSIONS = {'pdf', 'doc', 'docx'} def allowed_file(filename): """Check if the file has an allowed extension.""" return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @sops_bp.route('/get_roles', methods=['POST']) def get_roles(): # Check if the post request has the file part if 'document' not in request.files: return jsonify({"error": "No file part", "message": "Please upload a file with the key 'document'."}), 400 file = request.files['document'] # If the user does not select a file, the browser may also submit an empty part without filename if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) # Save the file to the upload folder file.save(file_path) try: # Use the utility function to generate docs from the file docs = load_document(file_path) # Generate roles from the docs roles = sop_generator.get_roles(docs)["roles"] # Cleanup: Delete all files in the upload directory after processing delete_all_files_in_directory(upload_folder) return jsonify({"roles": roles, "message": "Roles successfully extracted from the document."}), 200 except Exception as e: # Cleanup: Delete all files in the upload directory if an error occurs delete_all_files_in_directory(upload_folder) return jsonify({"error": "Processing error", "message": f"An error occurred while processing the document: {str(e)}"}), 500 return jsonify({"error": "File type not allowed", "message": "The uploaded file type is not allowed. Please upload a PDF, DOC, or DOCX file."}), 400 @sops_bp.route('/generate_questions_from_doc', methods=['POST']) def generate_questions_from_sop(): # Check if the POST request has the file part if 'document' not in request.files: return jsonify({"error": "No file part", "message": "Please upload a file with the key 'document'."}), 400 print("Running................") file = request.files['document'] roles_json = request.form.get('roles') # Get the roles as a JSON string if not roles_json: return jsonify({"error": "No roles provided", "message": "Please provide a list of roles in the 'roles' field."}), 400 try: roles = json.loads(roles_json) # Parse the roles from JSON string to a list print(f"Roles are:{roles}") except json.JSONDecodeError: return jsonify({"error": "Invalid JSON", "message": "The 'roles' field contains invalid JSON."}), 400 # If the user does not select a file, the browser may also submit an empty part without a filename if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) # Save the file to the upload folder file.save(file_path) try: # Use the utility function to generate docs from the file docs = load_document(file_path) # Check if the document can generate SOPs for the roles status_check = sop_generator.check_role_sop(roles=roles, docs=docs) if not status_check["status"]: return jsonify({"error": "Document cannot extract SOPs", "message": status_check["message"]}), 400 # Generate SOPs based on the roles provided sop_generator = DocumentParser() sops = sop_generator.generate_sops_from_doc(docs) # Cleanup: Delete all files in the upload directory after processing delete_all_files_in_directory(upload_folder) return jsonify({"sops": sops, "message": "SOPs successfully generated for the roles from the document."}), 200 except Exception as e: # Cleanup: Delete all files in the upload directory if an error occurs delete_all_files_in_directory(upload_folder) return jsonify({"error": "Processing error", "message": f"An error occurred while processing the document: {str(e)}"}), 500 return jsonify({"error": "File type not allowed", "message": "The uploaded file type is not allowed. Please upload a PDF, DOC, or DOCX file."}), 400 @sops_bp.route('/personal_assessment/generate_sops_from_doc', methods=['POST']) def generate_sops(): # Check if the POST request has the file part if 'document' not in request.files: return jsonify({"error": "No file part", "message": "Please upload a file with the key 'document'."}), 400 print("Running................") file = request.files['document'] # If the user does not select a file, the browser may also submit an empty part without a filename if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) # Save the file to the upload folder file.save(file_path) try: # Use the utility function to generate docs from the file docs = load_document(file_path) # Generate SOPs based on the roles provided sop_generator = DocumentParser() sops = sop_generator.extract_sops_from_doc(docs) # Cleanup: Delete all files in the upload directory after processing delete_all_files_in_directory(upload_folder) if not sops: return jsonify({"error":"Error in generating sops"}) return jsonify({"sops": sops, "message": "SOPs successfully generated for the roles from the document."}), 200 except Exception as e: # Cleanup: Delete all files in the upload directory if an error occurs delete_all_files_in_directory(upload_folder) return jsonify({"error": "Processing error", "message": f"An error occurred while processing the document: {str(e)}"}), 500 return jsonify({"error": "File type not allowed", "message": "The uploaded file type is not allowed. Please upload a PDF, DOC, or DOCX file."}), 400 @sops_bp.route('/personal_assessment/generate_sops_from_questionnaire', methods=['POST']) def generate_sops_from_questionnaire_per(): """ Generate SOPs based on the questionnaire data provided in the request body. The request body is expected to contain plain-text information for vision, roles, responsibilities, and project details. """ try: # Get the questionnaire data from the request body questionnaire_data = request.json # Validate the required fields in the questionnaire data if not questionnaire_data.get('vision') or not questionnaire_data.get('roles') or not questionnaire_data.get('responsibilities'): return jsonify({ "error": "Missing required fields", "message": "Please provide 'vision', 'roles', and 'responsibilities' in the request body." }), 400 # Step 1: Call the function from the sop_generator sop_generator = SopPersonalAssessment() sops_response = sop_generator.extract_sops_from_questionnaire(questionnaire_data) # Step 2: Return the SOPs if the extraction is successful if not sops_response: return jsonify({ "error": "SOP generation failed", "message": "Failed to generate SOPs based on the provided questionnaire data." }), 500 return jsonify({ "sops": sops_response, "message": "SOPs successfully generated based on the provided questionnaire data." }), 200 except Exception as e: return jsonify({ "error": "Processing error", "message": f"An error occurred while generating SOPs: {str(e)}" }), 500 @sops_bp.route('/personal_assessment/generate_sops_by_roles_and_areas', methods=['POST']) def generate_sops_by_roles_and_areas(): """ Generate SOPs based on the roles, SOP types (will, shall, must), and areas provided in the request body. """ try: # Get the roles data from the request body roles = request.json.get('roles', None) sop_generator = SopPersonalAssessment() # Validate the presence of roles data if not roles or not isinstance(roles, list): return jsonify({"error": "Invalid input", "message": "The 'roles' field should be a non-empty list."}), 400 # Generate SOPs for all roles at once sops_response = sop_generator.generate_sops_by_role_and_area(roles=roles) return jsonify({ "sops": sops_response, "message": "SOPs successfully generated for all provided roles." }), 200 except Exception as e: return jsonify({ "error": "Processing error", "message": f"An error occurred while generating SOPs: {str(e)}" }), 500 @sops_bp.route('/executive/generate_sop_mission_from_vision', methods=['POST']) def generate_executive_sops_from_doc(): """ Generate SOPs for executives based on a document containing vision and mission. """ # Check if the POST request has the file part and roles if 'document' not in request.files: return jsonify({"error": "No file part", "message": "Please upload a file with the key 'document'."}), 400 if 'executives' not in request.form: return jsonify({"error": "No roles provided", "message": "Please provide roles as a JSON array."}), 400 try: executives = request.form.get('executives') # Manually load roles from the string to JSON executives = json.loads(executives) except json.JSONDecodeError: return jsonify({"error": "Invalid JSON", "message": "The roles must be a valid JSON array."}), 400 except ValueError as e: return jsonify({"error": "Invalid roles format", "message": str(e)}), 400 file = request.files['document'] # If the user does not select a file, the browser may also submit an empty part without filename if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) # Save the file to the upload folder file.save(file_path) try: # Use the utility function to generate docs from the file docs = load_document(file_path) sop_doc = DocumentParser() vision_mission = sop_doc.extract_vision_mission(docs) if not vision_mission: return jsonify({"error": "Vision and Mission generation error ", "message": "Error in generating mssion and viso."}), 400 # Check if both vision and mission are empty if not vision_mission.get('vision') and not vision_mission.get('mission'): # Cleanup: Delete all files in the upload directory if parsing fails delete_all_files_in_directory(upload_folder) return jsonify({"error": "Missing Vision and Mission", "message": "The document does not contain mission and vision."}), 400 print(f"Vision and mission: {vision_mission}") ex_sop = SopGeneratorExecutive() result = {} for exe in executives: sops = ex_sop.extract_sops_from_executive_vision_goals_doc(vision_mission, exe) result[exe] = sops # Cleanup: Delete all files in the upload directory after processing delete_all_files_in_directory(upload_folder) return jsonify({"sops": result, "message": "SOPs successfully generated from the document."}), 200 except Exception as e: # Cleanup: Delete all files in the upload directory if an error occurs delete_all_files_in_directory(upload_folder) return jsonify({"error": "Processing error", "message": f"An error occurred while processing the document: {str(e)}"}), 500 return jsonify({"error": "File type not allowed", "message": "The uploaded file type is not allowed. Please upload a PDF, DOC, or DOCX file."}), 400 @sops_bp.route('/executive/generate_sop_managers_doc', methods=['POST']) def generate_sop_managers_doc(): if 'document' not in request.files: return jsonify({"error": "No file part", "message": "Please upload a file with the key 'document'."}), 400 file = request.files['document'] if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) file.save(file_path) try: docs = load_document(file_path) sop_generator = SopGeneratorExecutive() result = sop_generator.generate_sops_for_department_managers(docs) delete_all_files_in_directory(upload_folder) if not result: return jsonify({"error": "Processing error", "message": "Failed to generate SOPs for department managers."}), 500 return jsonify({"sops": result.dict(), "message": "SOPs successfully generated for department managers."}), 200 except Exception as e: delete_all_files_in_directory(upload_folder) return jsonify({"error": "Processing error", "message": f"An error occurred while processing the document: {str(e)}"}), 500 return jsonify({"error": "File type not allowed", "message": "The uploaded file type is not allowed. Please upload a PDF, DOC, or DOCX file."}), 400 @sops_bp.route('/executive/generate_sops_from_questionnaire', methods=['POST']) def generate_sops_from_questionnaire(): try: data = request.json questionnaire_data = data.get('questionnaire') executives = data.get('executives', []) managers = data.get('managers', []) departments = data.get('departments', []) if not questionnaire_data or not executives or not managers or not departments: return jsonify({"error": "Missing data", "message": "Please provide questionnaire data, executives, managers, and departments."}), 400 sop_generator = SopGeneratorExecutive() result = sop_generator.generate_sops_from_questionnaire(questionnaire_data, executives, managers, departments) if not result: return jsonify({"error": "Processing error", "message": "Failed to generate SOPs from questionnaire."}), 500 # Convert Pydantic models to dictionaries serializable_result = { "executive_sops": {exec: sops.dict() for exec, sops in result["executive_sops"].items()}, "department_sops": result["department_sops"].dict() } return jsonify({"sops": serializable_result, "message": "SOPs successfully generated from questionnaire."}), 200 except Exception as e: return jsonify({"error": "Processing error", "message": f"An error occurred while processing the request: {str(e)}"}), 500 @sops_bp.route('/executive/get_roles_for_reference_managers', methods=['POST']) def get_roles_for_reference_managers(): try: # Retrieve form data reference_roles_text = request.form.get('reference_roles') # Reference roles sent as text file = request.files.get('document') # The uploaded document if not reference_roles_text or not file: return jsonify({"error": "Missing data", "message": "Reference roles or document not provided."}), 400 if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 # Convert reference_roles_text to JSON (list) try: reference_roles = json.loads(reference_roles_text) # Convert text to JSON (list) except json.JSONDecodeError: return jsonify({"error": "Invalid format", "message": "The reference roles should be in a valid JSON format."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) file.save(file_path) docs = load_document(file_path) # Use extractor to extract roles from the document extractor = DocumentParser() roles_comparison_with_doc = extractor.extract_roles_with_reference_managers(docs=docs, reference_roles=reference_roles) if not roles_comparison_with_doc: return jsonify({"error": "Processing error", "message": "No roles found matching the reference roles."}), 404 return jsonify({"roles_info": roles_comparison_with_doc, "message": "Roles comparison successfully generated."}), 200 except Exception as e: return jsonify({"error": "Processing error", "message": f"An error occurred while processing the request: {str(e)}"}), 500 @sops_bp.route('/manager/get_roles_for_reference_workers', methods=['POST']) def get_roles_for_reference_workers(): try: # Retrieve form data reference_roles_text = request.form.get('reference_roles') # Reference roles sent as text file = request.files.get('document') # The uploaded document if not reference_roles_text or not file: return jsonify({"error": "Missing data", "message": "Reference roles or document not provided."}), 400 if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 # Convert reference_roles_text to JSON (list) try: reference_roles = json.loads(reference_roles_text) # Convert text to JSON (list) except json.JSONDecodeError: return jsonify({"error": "Invalid format", "message": "The reference roles should be in a valid JSON format."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) file.save(file_path) docs = load_document(file_path) # Use extractor to extract roles from the document extractor = DocumentParser() roles_comparison_with_doc = extractor.extract_roles_with_reference_workers(docs=docs, reference_roles=reference_roles) if not roles_comparison_with_doc: return jsonify({"error": "Processing error", "message": "No roles found matching the reference roles."}), 404 return jsonify({"roles_info": roles_comparison_with_doc, "message": "Roles comparison successfully generated."}), 200 except Exception as e: return jsonify({"error": "Processing error", "message": f"An error occurred while processing the request: {str(e)}"}), 500 @sops_bp.route('/manager/generate_sop_workers_doc', methods=['POST']) def generate_sop_workers_doc(): try: # Check if the document is provided if 'document' not in request.files: return jsonify({"error": "No file part", "message": "Please upload a file with the key 'document'."}), 400 file = request.files['document'] # Check if the file is selected if file.filename == '': return jsonify({"error": "No selected file", "message": "A file was not selected for upload. Please select a valid file."}), 400 # Check if the file type is allowed if file and allowed_file(file.filename): filename = secure_filename(file.filename) upload_folder = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(upload_folder, filename) file.save(file_path) else: return jsonify({"error": "File type not allowed", "message": "The uploaded file type is not allowed. Please upload a PDF, DOC, or DOCX file."}), 400 # Get workers information from the form data (passed as JSON text) workers_info_text = request.form.get('workers_info') if not workers_info_text: return jsonify({"error": "Missing data", "message": "Workers info is required in the form data."}), 400 # Load workers information as a list of dictionaries try: workers_list = json.loads(workers_info_text) except json.JSONDecodeError: return jsonify({"error": "Invalid data format", "message": "Workers info should be a valid JSON array."}), 400 # Now load the document docs = load_document(file_path) # Generate SOPs for workers by department using SopGeneratorExecutive sop_generator = DocumentParser() result = sop_generator.extract_sops_for_workers_by_department(docs, workers_list) # Clean up the file delete_all_files_in_directory(upload_folder) if not result: return jsonify({"error": "Processing error", "message": "Failed to generate SOPs for workers."}), 500 return jsonify({"sops": result, "message": "SOPs successfully generated for workers."}), 200 except Exception as e: delete_all_files_in_directory(upload_folder) return jsonify({"error": "Processing error", "message": f"An error occurred while processing the document: {str(e)}"}), 500