added sop pdf generator

This commit is contained in:
2025-05-12 21:55:50 +00:00
parent dcae438e64
commit d005200145
8 changed files with 579 additions and 149 deletions
+1
View File
@@ -4,3 +4,4 @@ langchain-openai
pydantic
flask
python-dotenv
reportlab
View File
+63 -4
View File
@@ -7,7 +7,11 @@ from src.utils.auth import auth_check
from src.utils.utils import delete_all_files_in_directory
from src.utils.document_loader import load_document
from flask import Blueprint, jsonify, request, make_response
import os
import tempfile
import datetime
from flask import send_file
from flask import Blueprint, jsonify, request, make_response,after_this_request
import json
# Initialize the Blueprint
sops_bp = Blueprint('sops', __name__)
@@ -71,6 +75,7 @@ def get_roles():
def get_roles_questionnaire():
# Check if the post request has the file part
questionnaire_data = request.json
role_slug = questionnaire_data.get("role_slug")
# Validate the required fields in the questionnaire data
if not questionnaire_data.get('questionnaire_response'):
@@ -81,7 +86,7 @@ def get_roles_questionnaire():
generator = SopPersonalAssessment()
roles = generator.generate_roles_from_questionnaire(questionnaire_data)
roles = generator.generate_roles_from_questionnaire(questionnaire_data,role_slug)
if not roles:
return jsonify({"error": "No roles found", "message": "No roles were extracted from the questionnaire."}), 404
@@ -89,8 +94,6 @@ def get_roles_questionnaire():
return jsonify({"roles": roles, "message": "Roles successfully extracted from the questionnaire."}), 200
@sops_bp.route('/personal_assessment/generate_sops_from_doc', methods=['POST'])
@auth_check
def generate_sops():
@@ -226,6 +229,62 @@ def generate_sops_by_roles_and_areas():
@sops_bp.route('/general/generate_sops_pdf', methods=['POST'])
@auth_check
def generate_sops_pdf():
"""
Generate a PDF file of SOPs based on the SOP JSON data provided in the request body.
Returns the PDF as a downloadable file and then deletes the temporary file.
"""
try:
# Get the SOP JSON data from the request body
sop_data = request.json
# Validate the presence of SOP data
if not sop_data or not isinstance(sop_data, dict) or 'sop_details' not in sop_data:
return make_response(jsonify({
"error": "Invalid input",
"message": "The request body should contain valid SOP data with a 'sop_details' field."
}), 400)
# Create a unique temporary filename for the PDF
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
temp_dir = tempfile.gettempdir()
pdf_filename = f"sop_document_{timestamp}.pdf"
pdf_path = os.path.join(temp_dir, pdf_filename)
# Import the PDF conversion function
from ...utils.sop_pdf_creator import convert_sop_to_pdf
# Generate the PDF file
convert_sop_to_pdf(sop_data, pdf_path, "")
# Send the file as a download attachment
@after_this_request
def remove_file(response):
try:
# Delete the temporary file after the response is sent
if os.path.exists(pdf_path):
os.remove(pdf_path)
except Exception as e:
print(f"Error removing temporary PDF file: {str(e)}")
return response
# Return the file as a downloadable attachment
return send_file(
pdf_path,
as_attachment=True,
download_name=f"SOP_Document_{timestamp}.pdf",
mimetype='application/pdf'
)
except Exception as e:
print(f"Error generating PDF: {str(e)}")
return make_response(jsonify({
"error": "Processing error",
"message": f"An error occurred while generating the SOP PDF: {str(e)}"
}), 500)
@sops_bp.route('/executive/generate_sop_mission_from_vision', methods=['POST'])
@auth_check
+1
View File
@@ -10,6 +10,7 @@ class Categories(BaseModel):
class RoleSops(BaseModel):
role:str
narrative:str
sops:Categories
class RoleSopssLists(BaseModel):
+28 -11
View File
@@ -15,19 +15,35 @@ def get_sop_extraction_from_doc():
def get_roles_extraction_from_questionnaire():
return '''Your task is to extract the "Roles" from the provided questionnaire responses.
You must identify and categorize the roles based on the information provided.
return '''
You are a specialized role extractor for company documents. Your task is to identify and extract job roles/positions mentioned in the provided Questionairre data.
TASK:
1. Extract ALL job roles/positions mentioned in the in questionairre response data as a list.
2. Filter the extracted roles based on the provided role_slug that will be provided.
3. Return the filtered roles as a JSON list.
RULES:
- Return an empty list if no matching roles are found.
- The role_slug is a keyword or category used to filter relevant roles.
- Only include roles that semantically relate to the role_slug.
- Be precise in extracting official job titles rather than general descriptions.
EXAMPLES:
Example 1:
Text: "Our company is looking to hire a Senior Data Scientist, Junior Data Analyst, and Database Administrator for the Analytics department. We also have openings for Financial Manager and Customer Support Manager."
Role_slug: "data"
Expected output: ["Senior Data Scientist", "Junior Data Analyst", "Database Administrator"]
Example 2:
Text: "The restructuring process will affect several departments including the Financial Analysis team, Customer Relations department, and Sales Management. We are currently seeking a Regional Sales Manager, Sales Team Supervisor, and Customer Support Manager."
Role_slug: "manager"
Expected output: ["Financial Manager", "Regional Sales Manager", "Customer Support Manager"]
Instructions:
1. **Roles**: Extract the roles mentioned in the questionnaire.
2. **Vision**: If applicable, extract the vision of the company or organization as it relates to the roles.
3. **Mission**: If applicable, extract the mission of the company or organization as it relates to the roles.
4. **Role-specific SOPs**:
- Identify any role-specific Standard Operating Procedures (SOPs) mentioned in the questionnaire.
- If SOPs for the role are not explicitly stated, infer them from the context, but only if there is clear evidence within the questionnaire. Do not generate or assume SOPs that are not directly supported by the information provided.
- If no roles or SOPs are found, return an empty list for each category.
Provide the extracted roles and any relevant sections exactly as they appear in the questionnaire.'''
'''
def get_sop_personalassessment_from_questionnaire():
@@ -89,6 +105,7 @@ def get_sop_personalassessment_from_area_rolev2():
NOTE: IF AREAS ARE NOT PROVIDED (AREA IS "NOT PROVIDED"), INTUITIVELY PROVIDE THE SOP BASED ON THE ROLE NAME.
NOTE: MAKE SURE SOPS ARE NOT MISSING FOR THE PROVIDED TYPES.
NOTE: FOR SOP TYPES NOT SELECTED RETURN AN EMPTY LIST and not "null" E.G IF "SHALL" AND "WILL" ARE SELECTED BUT "MUST" IS NOT AMONG, MUST WILL BE AN EMPTY LIST
also for each role , add a "narrative" which is short description of the role in question
NOTE !!!: IF A ROLE POINTS TO A SPECIFIC SOP TYPE (E.G., "SHALL" AND "MUST"), THESE TWO MUST NEVER BE EMPTY FOR THAT ROLE.
: FORMAT: SOPS SHOULD BE CLEAR, DIRECT, AND CONCISE. EACH ROLE SHOULD HAVE 5-7 BULLET POINTS PER SOP TYPE ("WILL," "SHALL," OR "MUST"). FOR COMPLEX ROLES, EACH SOP TYPE MAY HAVE A MAXIMUM OF 7-10 BULLET POINTS, NOT TOTAL ACROSS ALL TYPES, BUT PER SOP TYPE.
+6 -2
View File
@@ -123,7 +123,7 @@ class SopPersonalAssessment:
return False
def generate_roles_from_questionnaire(self, questionnaire_data: List[dict]) -> Roles_response:
def generate_roles_from_questionnaire(self, questionnaire_data: List[dict],role_slug:str) -> Roles_response:
try:
# List of areas: ["communication", "development", etc.]
@@ -139,10 +139,14 @@ class SopPersonalAssessment:
{
"role": "user",
"content": f'''Questionairre data : {questionnaire_data}''',
},
{
"role": "user",
"content": f'''Role slug to consider : {role_slug}''',
}
],
response_format=Roles_response,
max_tokens=16000,
max_tokens=4096,
temperature=0.1
)
extracted_text = json.loads(response.choices[0].message.content)
+244
View File
@@ -0,0 +1,244 @@
import json
import datetime
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Image
from reportlab.platypus import Frame, PageTemplate, NextPageTemplate
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT, TA_RIGHT
from reportlab.lib.units import inch, cm
from reportlab.pdfgen import canvas
def header_footer(canvas, doc):
"""Add the header and footer to each page"""
canvas.saveState()
# Header
header_text = "Standard Operating Procedures"
canvas.setFont('Helvetica-Bold', 10)
canvas.drawString(72, letter[1] - 40, header_text)
# Add a line below the header
canvas.setStrokeColor(colors.lightgrey)
canvas.line(72, letter[1] - 50, letter[0] - 72, letter[1] - 50)
# Footer with page number and date
current_date = datetime.datetime.now().strftime("%B %d, %Y")
page_num = f"Page {doc.page} | {current_date}"
canvas.setFont('Helvetica', 8)
canvas.drawString(letter[0] - 150, 40, page_num)
# Add a line above the footer
canvas.line(72, 50, letter[0] - 72, 50)
canvas.restoreState()
def convert_sop_to_pdf(sop_data, output_pdf="sop_document.pdf", company_name="Company Name"):
"""
Convert SOP data to a well-formatted PDF document
Args:
sop_data (dict or str): SOP data in dictionary format or JSON string
output_pdf (str): Output PDF filename
company_name (str): Name of the company to display on the cover page
"""
# Parse JSON string if needed
if isinstance(sop_data, str):
sop_data = json.loads(sop_data)
# Extract SOP details
sop_details = sop_data.get("sop_details", [])
# Create PDF document
doc = SimpleDocTemplate(output_pdf, pagesize=letter,
rightMargin=72, leftMargin=72,
topMargin=90, bottomMargin=72)
# Container for PDF elements
elements = []
# Get styles
styles = getSampleStyleSheet()
# Define custom styles
title_style = ParagraphStyle(
'TitleStyle',
parent=styles['Heading1'],
fontSize=24,
alignment=TA_CENTER,
spaceAfter=12,
fontName='Helvetica-Bold',
textColor=colors.darkblue
)
subtitle_style = ParagraphStyle(
'SubtitleStyle',
parent=styles['Heading2'],
fontSize=16,
alignment=TA_CENTER,
spaceAfter=36,
fontName='Helvetica-Bold',
textColor=colors.darkblue
)
heading2_style = ParagraphStyle(
'Heading2Style',
parent=styles['Heading2'],
fontSize=16,
spaceBefore=12,
spaceAfter=6,
fontName='Helvetica-Bold',
textColor=colors.darkblue,
borderWidth=0,
borderColor=colors.lightgrey,
borderPadding=5,
borderRadius=5
)
heading3_style = ParagraphStyle(
'Heading3Style',
parent=styles['Heading3'],
fontSize=12,
spaceBefore=6,
spaceAfter=3,
fontName='Helvetica-Bold',
textColor=colors.darkslategray
)
normal_style = ParagraphStyle(
'NormalStyle',
parent=styles['Normal'],
fontSize=10,
alignment=TA_JUSTIFY,
leading=14,
fontName='Helvetica'
)
bullet_style = ParagraphStyle(
'BulletStyle',
parent=normal_style,
leftIndent=20,
firstLineIndent=-15,
spaceBefore=2,
spaceAfter=2
)
# Create cover page
elements.append(Spacer(1, 2*inch))
elements.append(Paragraph("Standard Operating Procedures", title_style))
elements.append(Spacer(1, 0.5*inch))
#elements.append(Paragraph(company_name, subtitle_style))
elements.append(Spacer(1, 2*inch))
# Add current date
current_date = datetime.datetime.now().strftime("%B %d, %Y")
date_style = ParagraphStyle(
'DateStyle',
parent=styles['Normal'],
fontSize=12,
alignment=TA_CENTER,
fontName='Helvetica'
)
elements.append(Paragraph(f"Generated on: {current_date}", date_style))
# Add a page break after the cover page
elements.append(PageBreak())
# Process each role
for role_data in sop_details:
role_name = role_data.get("role", "Unnamed Role")
sops = role_data.get("sops", {})
narrative = role_data.get("narrative")
# Add role header with decorative element
elements.append(Paragraph(role_name, heading2_style))
# Add horizontal rule after heading
elements.append(Spacer(1, 0.05*inch))
# Add narrative if available
if narrative and narrative != "Narrative" and narrative is not None:
elements.append(Paragraph("Narrative:", heading3_style))
elements.append(Paragraph(narrative, normal_style))
elements.append(Spacer(1, 0.2*inch))
# Process SOPs
for sop_type in ["must", "shall", "will"]:
sop_items = sops.get(sop_type, [])
if sop_items:
# Capitalize the first letter of SOP type and make it bold
sop_type_title = sop_type.capitalize()
elements.append(Paragraph(f"{sop_type_title}:", heading3_style))
# Create bullet points for each SOP item with better formatting
for item in sop_items:
elements.append(Paragraph(f"{item}", bullet_style))
elements.append(Spacer(1, 0.15*inch))
# Add a page break between roles for cleaner separation
elements.append(PageBreak())
# Build the PDF document with header and footer
doc.build(elements, onFirstPage=header_footer, onLaterPages=header_footer)
return output_pdf
def main():
# Example usage
sop_json = """
{
"sop_details": [
{
"role": "Sales Manager",
"role_id": 140,
"sops": {
"must": [],
"shall": [
"Shall develop and implement sales strategies to achieve company targets.",
"Shall conduct regular performance reviews with the sales team to ensure targets are met.",
"Shall provide training and support to sales staff to enhance their skills and performance.",
"Shall analyze market trends and adjust sales strategies accordingly.",
"Shall maintain accurate records of sales activities and customer interactions."
],
"will": [
"Will lead the sales team to achieve monthly and quarterly sales goals.",
"Will collaborate with marketing to align sales strategies with promotional campaigns.",
"Will report sales performance to upper management on a regular basis.",
"Will identify potential new markets and customer segments for growth.",
"Will foster a positive team environment to motivate sales staff."
]
},
"areas": [],
"narrative": "The Sales Manager is responsible for leading and developing the sales team to achieve business targets and growth objectives. They will implement effective sales strategies, provide coaching to team members, and maintain strong customer relationships while ensuring all sales activities align with the company's overall goals."
},
{
"role": "Campaign Manager",
"role_id": 141,
"sops": {
"must": [],
"shall": [],
"will": [
"Will develop and execute marketing campaigns to promote products and services.",
"Will analyze campaign performance metrics to optimize future campaigns.",
"Will collaborate with cross-functional teams to ensure campaign alignment with business objectives.",
"Will manage campaign budgets and ensure effective allocation of resources.",
"Will stay updated on industry trends to inform campaign strategies."
]
},
"areas": [],
"narrative": "The Campaign Manager oversees the planning, execution, and analysis of marketing campaigns across various channels to drive brand awareness and customer acquisition. They work closely with creative teams, external vendors, and stakeholders to ensure campaigns are effective, on-brand, and deliver measurable results."
}
]
}
"""
# You can replace the company name with your own
output_file = convert_sop_to_pdf(sop_json, company_name="Strategic Business Solutions, Inc.")
print(f"PDF successfully generated: {output_file}")
if __name__ == "__main__":
main()
+237 -133
View File
@@ -1,140 +1,244 @@
import requests
import pandas as pd
from dotenv import load_dotenv
load_dotenv()
import os
import json
import datetime
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Image
from reportlab.platypus import Frame, PageTemplate, NextPageTemplate
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT, TA_RIGHT
from reportlab.lib.units import inch, cm
from reportlab.pdfgen import canvas
DATA_KEY = os.getenv("AI_DATA_KEY")
# Constants for API requests
URL = "https://erpai.mkdlabs.com//v3/api/custom/erpai/common/get-data-ai"
HEADERS = {
"x-project": DATA_KEY # Replace with your actual key
}
def header_footer(canvas, doc):
"""Add the header and footer to each page"""
canvas.saveState()
# JSON bodies for API requests
def create_json_body(area_type, company_id):
return {
"type": area_type,
"options": {
"company_id": company_id
}
# Header
header_text = "Standard Operating Procedures"
canvas.setFont('Helvetica-Bold', 10)
canvas.drawString(72, letter[1] - 40, header_text)
# Add a line below the header
canvas.setStrokeColor(colors.lightgrey)
canvas.line(72, letter[1] - 50, letter[0] - 72, letter[1] - 50)
# Footer with page number and date
current_date = datetime.datetime.now().strftime("%B %d, %Y")
page_num = f"Page {doc.page} | {current_date}"
canvas.setFont('Helvetica', 8)
canvas.drawString(letter[0] - 150, 40, page_num)
# Add a line above the footer
canvas.line(72, 50, letter[0] - 72, 50)
canvas.restoreState()
def convert_sop_to_pdf(sop_data, output_pdf="sop_document.pdf", company_name="Company Name"):
"""
Convert SOP data to a well-formatted PDF document
Args:
sop_data (dict or str): SOP data in dictionary format or JSON string
output_pdf (str): Output PDF filename
company_name (str): Name of the company to display on the cover page
"""
# Parse JSON string if needed
if isinstance(sop_data, str):
sop_data = json.loads(sop_data)
# Extract SOP details
sop_details = sop_data.get("sop_details", [])
# Create PDF document
doc = SimpleDocTemplate(output_pdf, pagesize=letter,
rightMargin=72, leftMargin=72,
topMargin=90, bottomMargin=72)
# Container for PDF elements
elements = []
# Get styles
styles = getSampleStyleSheet()
# Define custom styles
title_style = ParagraphStyle(
'TitleStyle',
parent=styles['Heading1'],
fontSize=24,
alignment=TA_CENTER,
spaceAfter=12,
fontName='Helvetica-Bold',
textColor=colors.darkblue
)
subtitle_style = ParagraphStyle(
'SubtitleStyle',
parent=styles['Heading2'],
fontSize=16,
alignment=TA_CENTER,
spaceAfter=36,
fontName='Helvetica-Bold',
textColor=colors.darkblue
)
heading2_style = ParagraphStyle(
'Heading2Style',
parent=styles['Heading2'],
fontSize=16,
spaceBefore=12,
spaceAfter=6,
fontName='Helvetica-Bold',
textColor=colors.darkblue,
borderWidth=0,
borderColor=colors.lightgrey,
borderPadding=5,
borderRadius=5
)
heading3_style = ParagraphStyle(
'Heading3Style',
parent=styles['Heading3'],
fontSize=12,
spaceBefore=6,
spaceAfter=3,
fontName='Helvetica-Bold',
textColor=colors.darkslategray
)
normal_style = ParagraphStyle(
'NormalStyle',
parent=styles['Normal'],
fontSize=10,
alignment=TA_JUSTIFY,
leading=14,
fontName='Helvetica'
)
bullet_style = ParagraphStyle(
'BulletStyle',
parent=normal_style,
leftIndent=20,
firstLineIndent=-15,
spaceBefore=2,
spaceAfter=2
)
# Create cover page
elements.append(Spacer(1, 2*inch))
elements.append(Paragraph("Standard Operating Procedures", title_style))
elements.append(Spacer(1, 0.5*inch))
#elements.append(Paragraph(company_name, subtitle_style))
elements.append(Spacer(1, 2*inch))
# Add current date
current_date = datetime.datetime.now().strftime("%B %d, %Y")
date_style = ParagraphStyle(
'DateStyle',
parent=styles['Normal'],
fontSize=12,
alignment=TA_CENTER,
fontName='Helvetica'
)
elements.append(Paragraph(f"Generated on: {current_date}", date_style))
# Add a page break after the cover page
elements.append(PageBreak())
# Process each role
for role_data in sop_details:
role_name = role_data.get("role", "Unnamed Role")
sops = role_data.get("sops", {})
narrative = role_data.get("narrative")
# Add role header with decorative element
elements.append(Paragraph(role_name, heading2_style))
# Add horizontal rule after heading
elements.append(Spacer(1, 0.05*inch))
# Add narrative if available
if narrative and narrative != "Narrative" and narrative is not None:
elements.append(Paragraph("Narrative:", heading3_style))
elements.append(Paragraph(narrative, normal_style))
elements.append(Spacer(1, 0.2*inch))
# Process SOPs
for sop_type in ["must", "shall", "will"]:
sop_items = sops.get(sop_type, [])
if sop_items:
# Capitalize the first letter of SOP type and make it bold
sop_type_title = sop_type.capitalize()
elements.append(Paragraph(f"{sop_type_title}:", heading3_style))
# Create bullet points for each SOP item with better formatting
for item in sop_items:
elements.append(Paragraph(f"{item}", bullet_style))
elements.append(Spacer(1, 0.15*inch))
# Add a page break between roles for cleaner separation
elements.append(PageBreak())
# Build the PDF document with header and footer
doc.build(elements, onFirstPage=header_footer, onLaterPages=header_footer)
return output_pdf
def main():
# Example usage
sop_json = """
{
"sop_details": [
{
"role": "Sales Manager",
"role_id": 140,
"sops": {
"must": [],
"shall": [
"Shall develop and implement sales strategies to achieve company targets.",
"Shall conduct regular performance reviews with the sales team to ensure targets are met.",
"Shall provide training and support to sales staff to enhance their skills and performance.",
"Shall analyze market trends and adjust sales strategies accordingly.",
"Shall maintain accurate records of sales activities and customer interactions."
],
"will": [
"Will lead the sales team to achieve monthly and quarterly sales goals.",
"Will collaborate with marketing to align sales strategies with promotional campaigns.",
"Will report sales performance to upper management on a regular basis.",
"Will identify potential new markets and customer segments for growth.",
"Will foster a positive team environment to motivate sales staff."
]
},
"areas": [],
"narrative": "The Sales Manager is responsible for leading and developing the sales team to achieve business targets and growth objectives. They will implement effective sales strategies, provide coaching to team members, and maintain strong customer relationships while ensuring all sales activities align with the company's overall goals."
},
{
"role": "Campaign Manager",
"role_id": 141,
"sops": {
"must": [],
"shall": [],
"will": [
"Will develop and execute marketing campaigns to promote products and services.",
"Will analyze campaign performance metrics to optimize future campaigns.",
"Will collaborate with cross-functional teams to ensure campaign alignment with business objectives.",
"Will manage campaign budgets and ensure effective allocation of resources.",
"Will stay updated on industry trends to inform campaign strategies."
]
},
"areas": [],
"narrative": "The Campaign Manager oversees the planning, execution, and analysis of marketing campaigns across various channels to drive brand awareness and customer acquisition. They work closely with creative teams, external vendors, and stakeholders to ensure campaigns are effective, on-brand, and deliver measurable results."
}
]
}
"""
# Function to fetch data from the API
def fetch_data(json_body):
json_body["options"]["company_id"] = json_body["options"].get("company_id") # Ensure company_id is included
response = requests.post(URL, headers=HEADERS, json=json_body)
response.raise_for_status() # Raise an error for bad responses
return response.json()
def convert_assessment_data_to_dataframe(assessment_data):
df_assessment = []
for assessment in assessment_data.get("data", []):
assessment_id = assessment["assessment_id"]
assessment_name = assessment["assessment_name"]
start_date = assessment["start_date"]
open_items = assessment["open_items"]
completed_items = assessment["completed_items"]
total_assigned_items = assessment["total_assigned_items"]
red_flags = assessment["red_flags"]
for user in assessment.get("user_details", []):
user_name = user["name"]
user_total_items = user["total_assigned_items"]
user_completed_items = user["completed_items"]
for area in user.get("area_list", []):
df_assessment.append({
"assessment_id": assessment_id,
"assessment_name": assessment_name,
"start_date": start_date,
"open_items_overall": open_items,
"completed_items_overall": completed_items,
"total_assigned_items_overall": total_assigned_items,
"user_name": user_name,
"user_total_assigned_items": user_total_items,
"user_completed_items": user_completed_items,
"area": area,
"red_flags": red_flags
})
return pd.DataFrame(df_assessment)
# Convert to DataFrame
# Summary statistics for overall assessment level
def generate_summary_statistics(df):
total_assessments = df['assessment_id'].nunique()
avg_open_items = df.groupby('assessment_id')['open_items_overall'].mean().mean()
avg_completed_items = df.groupby('assessment_id')['completed_items_overall'].mean().mean()
avg_total_assigned_items = df.groupby('assessment_id')['total_assigned_items_overall'].mean().mean()
avg_red_flags = df['red_flags'].mean()
total_users = df['user_name'].nunique()
avg_user_total_items = df.groupby('user_name')['user_total_assigned_items'].mean().mean()
avg_user_completed_items = df.groupby('user_name')['user_completed_items'].mean().mean()
completion_rate_per_user = (df['user_completed_items'].sum() / df['user_total_assigned_items'].sum()) * 100 if df['user_total_assigned_items'].sum() > 0 else 0
area_summary = df['area'].value_counts()
return {
"total_assessments": total_assessments,
"avg_open_items_per_assessment": avg_open_items,
"avg_completed_items_per_assessment": avg_completed_items,
"avg_total_assigned_items_per_assessment": avg_total_assigned_items,
"avg_red_flags": avg_red_flags,
"total_users": total_users,
"avg_user_total_assigned_items": avg_user_total_items,
"avg_user_completed_items": avg_user_completed_items,
"completion_rate_per_user": completion_rate_per_user,
"area_summary": area_summary.to_dict()
}
# Additional statistics for efficiency and areas
def generate_extended_statistics(df):
df['user_completion_rate'] = (df['user_completed_items'] / df['user_total_assigned_items']).fillna(0) * 100
top_5_efficient_users = df.groupby('user_name')['user_completion_rate'].mean().nlargest(5).to_dict()
bottom_5_least_efficient_users = df.groupby('user_name')['user_completion_rate'].mean().nsmallest(5).to_dict()
df['uncompleted_items'] = df['user_total_assigned_items'] - df['user_completed_items']
areas_with_most_uncompleted_items = df.groupby('area')['uncompleted_items'].sum().nlargest(5).to_dict()
return {
"top_5_efficient_users": top_5_efficient_users,
"bottom_5_least_efficient_users": bottom_5_least_efficient_users,
"areas_with_most_uncompleted_items": areas_with_most_uncompleted_items
}
# Generate statistics for problematic areas
def generate_problematic_area_statistics(df):
total_open_items = df.groupby('name')['open_items'].sum().sort_values(ascending=False)
total_red_flags = df.groupby('name')['red_flags'].sum().sort_values(ascending=False)
return pd.DataFrame({
"total_open_items": total_open_items,
"total_red_flags": total_red_flags
}).fillna(0)
def generate_summary_stats(assessment_data, area_data):
assessment_df = convert_assessment_data_to_dataframe(assessment_data)
problematic_area_df = pd.DataFrame(area_data.get("data", []))
summary_stats = generate_summary_statistics(assessment_df)
extended_stats = generate_extended_statistics(assessment_df)
summary_stats["users(Workers) based stats"] = extended_stats
problematic_stats = generate_problematic_area_statistics(problematic_area_df)
summary_stats["Area based stats"] = problematic_stats.to_dict(orient='index')
return summary_stats
# You can replace the company name with your own
output_file = convert_sop_to_pdf(sop_json, company_name="Strategic Business Solutions, Inc.")
print(f"PDF successfully generated: {output_file}")
if __name__ == "__main__":
from src.services.chatbot import Chatbot
bot = Chatbot()
res = bot.predict_next_n_assessment(companyid=12,N=3)
main()
print(res)