norhan12 commited on
Commit
4c8be03
·
verified ·
1 Parent(s): 6f2861b

Update process_interview.py

Browse files
Files changed (1) hide show
  1. process_interview.py +107 -124
process_interview.py CHANGED
@@ -592,11 +592,12 @@ def calculate_acceptance_probability(analysis_data: Dict) -> float:
592
 
593
 
594
 
 
595
  def generate_report(analysis_data: Dict) -> str:
596
  try:
597
  voice = analysis_data.get('voice_analysis', {})
598
  voice_interpretation = generate_voice_interpretation(voice)
599
- interviewee_responses = [f"- {u['text']}" for u in analysis_data['transcript'] if u.get('role') == 'Interviewee'][:5]
600
  acceptance_prob = analysis_data.get('acceptance_probability', 50.0)
601
  acceptance_line = f"\n**Suitability Score: {acceptance_prob:.2f}%**\n"
602
  if acceptance_prob >= 80:
@@ -608,38 +609,40 @@ def generate_report(analysis_data: Dict) -> str:
608
  else:
609
  acceptance_line += "HR Verdict: Limited fit, significant improvement required."
610
 
611
- # Enhanced prompt with detailed recommendations
612
  prompt = f"""
613
  You are EvalBot, a senior HR consultant delivering a comprehensive, professional interview analysis report.
614
- Use clear headings, bullet points ('-'), and avoid redundancy.
615
  Ensure text is clean, professional, and free of special characters that could break formatting.
616
  The interview involves two roles: Interviewer and Interviewee, assigned alternately to utterances.
 
617
  {acceptance_line}
618
  **1. Executive Summary**
619
- - Summarize the candidate's overall performance, key strengths, and hiring potential in 2-3 bullets.
620
  - Include metrics: Duration ({analysis_data['text_analysis']['total_duration']:.2f} seconds), Speaker Turns ({analysis_data['text_analysis']['speaker_turns']}).
621
  - Mention roles: Interviewer and Interviewee.
 
622
  **2. Communication and Vocal Dynamics**
623
- - Evaluate the Interviewee's vocal delivery (speaking rate, fluency, confidence, anxiety) in 2-3 bullets.
624
- - Provide one HR insight on workplace communication alignment.
625
  - Voice Analysis: {voice_interpretation}
626
  **3. Competency and Content**
627
- - List 2-3 key strengths (e.g., leadership, problem-solving, communication) with specific examples from responses.
628
- - List 1-2 weaknesses (growth areas) with actionable feedback to address them.
629
- - Sample Interviewee responses (for context):
630
  {chr(10).join(interviewee_responses)}
631
  **4. Role Fit and Potential**
632
- - Analyze the Interviewee's cultural fit, role readiness, and long-term growth potential in 2-3 bullets.
 
633
  **5. Recommendations**
634
- - Provide 3-4 specific suggestions for improvement focusing on:
635
- - Communication skills (e.g., clarity, fluency, reducing filler words).
636
- - Content delivery (e.g., structuring responses, emphasizing key points).
637
- - Professional presentation (e.g., confidence, tone, engagement).
638
- - Include practical strategies or examples for each suggestion.
639
- - Suggest 2-3 specific next steps for hiring managers (e.g., advance to technical round, provide training, conduct behavioral assessment).
640
  """
641
  response = gemini_model.generate_content(prompt)
642
- # Robust sanitization
643
  clean_text = re.sub(r'[^\x20-\x7E\n]+', '', response.text)
644
  return clean_text
645
  except Exception as e:
@@ -659,83 +662,23 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
659
  styles = getSampleStyleSheet()
660
 
661
  # Custom styles
662
- cover_title = ParagraphStyle(
663
- name='CoverTitle',
664
- fontSize=24,
665
- leading=28,
666
- spaceAfter=20,
667
- alignment=1,
668
- textColor=colors.HexColor('#003087'),
669
- fontName='Helvetica-Bold'
670
- )
671
- h1 = ParagraphStyle(
672
- name='Heading1',
673
- fontSize=16,
674
- leading=20,
675
- spaceAfter=14,
676
- alignment=1,
677
- textColor=colors.HexColor('#003087'),
678
- fontName='Helvetica-Bold'
679
- )
680
- h2 = ParagraphStyle(
681
- name='Heading2',
682
- fontSize=12,
683
- leading=15,
684
- spaceBefore=10,
685
- spaceAfter=8,
686
- textColor=colors.HexColor('#0050BC'),
687
- fontName='Helvetica-Bold'
688
- )
689
- h3 = ParagraphStyle(
690
- name='Heading3',
691
- fontSize=10,
692
- leading=12,
693
- spaceBefore=8,
694
- spaceAfter=6,
695
- textColor=colors.HexColor('#3F7CFF'),
696
- fontName='Helvetica-Bold'
697
- )
698
- body_text = ParagraphStyle(
699
- name='BodyText',
700
- fontSize=9,
701
- leading=12,
702
- spaceAfter=6,
703
- fontName='Helvetica',
704
- textColor=colors.HexColor('#333333')
705
- )
706
- bullet_style = ParagraphStyle(
707
- name='Bullet',
708
- parent=body_text,
709
- leftIndent=18,
710
- bulletIndent=8,
711
- fontName='Helvetica',
712
- bulletFontName='Helvetica',
713
- bulletFontSize=9
714
- )
715
- table_header = ParagraphStyle(
716
- name='TableHeader',
717
- fontSize=9,
718
- leading=11,
719
- textColor=colors.white,
720
- fontName='Helvetica-Bold'
721
- )
722
- table_body = ParagraphStyle(
723
- name='TableBody',
724
- fontSize=9,
725
- leading=11,
726
- fontName='Helvetica'
727
- )
728
 
729
  story = []
730
 
731
  def header_footer(canvas, doc):
732
  canvas.saveState()
733
- # Footer
734
  canvas.setFont('Helvetica', 8)
735
  canvas.setFillColor(colors.HexColor('#666666'))
736
  canvas.drawString(doc.leftMargin, 0.5*inch, f"Page {doc.page} | EvalBot HR Interview Report | Confidential")
737
  canvas.drawRightString(doc.width + doc.leftMargin, 0.5*inch, time.strftime('%B %d, %Y'))
738
- # Header
739
  canvas.setStrokeColor(colors.HexColor('#0050BC'))
740
  canvas.setLineWidth(0.8)
741
  canvas.line(doc.leftMargin, doc.height + 0.9*inch, doc.width + doc.leftMargin, doc.height + 0.9*inch)
@@ -754,34 +697,46 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
754
  story.append(Paragraph(f"Candidate ID: {analysis_data.get('user_id', 'N/A')}", body_text))
755
  story.append(Paragraph(f"Generated: {time.strftime('%B %d, %Y')}", body_text))
756
  story.append(Spacer(1, 0.5*inch))
757
- story.append(Paragraph("Confidential", ParagraphStyle(
758
- name='Confidential',
759
- fontSize=10,
760
- alignment=1,
761
- textColor=colors.HexColor('#D32F2F'),
762
- fontName='Helvetica-Bold'
763
- )))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
  story.append(PageBreak())
765
 
766
  # Title Page
767
  story.append(Paragraph("Interview Evaluation Summary", h1))
768
  story.append(Spacer(1, 0.3*inch))
769
- # Suitability Score
770
  acceptance_prob = analysis_data.get('acceptance_probability', 50.0)
771
  prob_color = colors.HexColor('#2E7D32') if acceptance_prob >= 80 else (
772
  colors.HexColor('#F57C00') if acceptance_prob >= 60 else colors.HexColor('#D32F2F')
773
  )
774
  story.append(Paragraph(
775
  f"Suitability Score: <font size=14 color='{prob_color.hexval()}'><b>{acceptance_prob:.2f}%</b></font>",
776
- ParagraphStyle(
777
- name='Score',
778
- fontSize=14,
779
- spaceAfter=12,
780
- alignment=1,
781
- fontName='Helvetica-Bold'
782
- )
783
  ))
784
- # HR Verdict
785
  if acceptance_prob >= 80:
786
  story.append(Paragraph("<b>HR Verdict:</b> Outstanding candidate, recommended for immediate advancement.", body_text))
787
  elif acceptance_prob >= 60:
@@ -792,7 +747,6 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
792
  story.append(Paragraph("<b>HR Verdict:</b> Limited fit, significant improvement required.", body_text))
793
  story.append(Spacer(1, 0.2*inch))
794
 
795
- # Summary Table with Roles
796
  roles = sorted(set(u.get('role', 'Unknown') for u in analysis_data.get('transcript', [])))
797
  table_data = [
798
  [Paragraph('Metric', table_header), Paragraph('Value', table_header)],
@@ -808,8 +762,8 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
808
  ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
809
  ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
810
  ('FONTSIZE', (0,0), (-1,-1), 9),
811
- ('BOTTOMPADDING', (0,0), (-1,0), 8),
812
- ('TOPPADDING', (0,0), (-1,0), 8),
813
  ('BACKGROUND', (0,1), (-1,-1), colors.HexColor('#F5F6FA')),
814
  ('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#DDE4EE')),
815
  ]))
@@ -822,7 +776,7 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
822
  story.append(Paragraph("Detailed Candidate Evaluation", h1))
823
 
824
  # Communication and Vocal Dynamics
825
- story.append(Paragraph("1. Communication & Vocal Dynamics", h2))
826
  voice_analysis = analysis_data.get('voice_analysis', {})
827
  if voice_analysis and 'error' not in voice_analysis:
828
  table_data = [
@@ -895,14 +849,14 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
895
  if not clean_line: continue
896
  clean_line = re.sub(r'[()\[\]{}]', '', clean_line)
897
  if current_section == 'Competency':
898
- if any(k in clean_line.lower() for k in ['leader', 'problem', 'commun', 'adapt', 'strength', 'effective', 'skill']):
899
  current_subsection = 'Strengths'
900
- elif any(k in clean_line.lower() for k in ['improv', 'grow', 'weak', 'depth', 'challenge']):
901
  current_subsection = 'Weaknesses'
902
  if current_subsection:
903
  sections[current_section][current_subsection].append(clean_line)
904
  elif current_section == 'Recommendations':
905
- if any(k in clean_line.lower() for k in ['commun', 'tech', 'depth', 'pres', 'improve', 'enhance', 'clarity', 'structur', 'tone']):
906
  current_subsection = 'Development'
907
  elif any(k in clean_line.lower() for k in ['adv', 'train', 'assess', 'next', 'mentor', 'round']):
908
  current_subsection = 'Next Steps'
@@ -911,13 +865,13 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
911
  else:
912
  sections[current_section].append(clean_line)
913
 
914
- # Summary Box
915
- story.append(Paragraph("Key Highlights", h2))
916
  summary_data = [
917
  [Paragraph("Category", table_header), Paragraph("Detail", table_header)],
918
- [Paragraph("Top Strength", table_body), Paragraph(sections['Competency']['Strengths'][0] if sections['Competency']['Strengths'] else "N/A", table_body)],
919
- [Paragraph("Key Weakness", table_body), Paragraph(sections['Competency']['Weaknesses'][0] if sections['Competency']['Weaknesses'] else "N/A", table_body)],
920
- [Paragraph("Top Recommendation", table_body), Paragraph(sections['Recommendations']['Development'][0] if sections['Recommendations']['Development'] else "N/A", table_body)],
921
  ]
922
  summary_table = Table(summary_data, colWidths=[2*inch, 4*inch])
923
  summary_table.setStyle(TableStyle([
@@ -936,16 +890,22 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
936
  story.append(Spacer(1, 0.3*inch))
937
 
938
  # Executive Summary
939
- story.append(Paragraph("2. Executive Summary", h2))
940
  if sections['Executive Summary']:
941
  for line in sections['Executive Summary']:
942
  story.append(Paragraph(line, bullet_style))
943
  else:
944
- story.append(Paragraph("No summary provided.", body_text))
 
 
 
 
 
 
945
  story.append(Spacer(1, 0.2*inch))
946
 
947
  # Competency and Content
948
- story.append(Paragraph("3. Competency & Evaluation", h2))
949
  story.append(Paragraph("Strengths", h3))
950
  if sections['Competency']['Strengths']:
951
  strength_table = Table([[Paragraph(line, bullet_style)] for line in sections['Competency']['Strengths']], colWidths=[6*inch])
@@ -956,7 +916,7 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
956
  ]))
957
  story.append(strength_table)
958
  else:
959
- story.append(Paragraph("No strengths identified.", body_text))
960
  story.append(Spacer(1, 0.1*inch))
961
  story.append(Paragraph("Weaknesses", h3))
962
  if sections['Competency']['Weaknesses']:
@@ -968,20 +928,26 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
968
  ]))
969
  story.append(weakness_table)
970
  else:
971
- story.append(Paragraph("No weaknesses identified; maintain current strengths.", body_text))
972
  story.append(Spacer(1, 0.2*inch))
973
 
974
  # Role Fit
975
- story.append(Paragraph("4. Role Fit & Potential", h2))
976
  if sections['Role Fit']:
977
  for line in sections['Role Fit']:
978
  story.append(Paragraph(line, bullet_style))
979
  else:
980
- story.append(Paragraph("No fit analysis provided.", body_text))
 
 
 
 
 
 
981
  story.append(Spacer(1, 0.2*inch))
982
 
983
  # Recommendations
984
- story.append(Paragraph("5. Recommendations", h2))
985
  story.append(Paragraph("Development Priorities", h3))
986
  if sections['Recommendations']['Development']:
987
  dev_table = Table([[Paragraph(line, bullet_style)] for line in sections['Recommendations']['Development']], colWidths=[6*inch])
@@ -992,14 +958,32 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
992
  ]))
993
  story.append(dev_table)
994
  else:
995
- story.append(Paragraph("No development priorities specified.", body_text))
 
 
 
 
 
 
 
 
 
 
 
 
996
  story.append(Spacer(1, 0.1*inch))
997
  story.append(Paragraph("Next Steps", h3))
998
  if sections['Recommendations']['Next Steps']:
999
  for line in sections['Recommendations']['Next Steps']:
1000
  story.append(Paragraph(line, bullet_style))
1001
  else:
1002
- story.append(Paragraph("No next steps provided.", body_text))
 
 
 
 
 
 
1003
  story.append(Spacer(1, 0.2*inch))
1004
 
1005
  doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
@@ -1010,7 +994,6 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
1010
  return False
1011
 
1012
 
1013
-
1014
  def convert_to_serializable(obj):
1015
  if isinstance(obj, np.generic):
1016
  return obj.item()
 
592
 
593
 
594
 
595
+
596
  def generate_report(analysis_data: Dict) -> str:
597
  try:
598
  voice = analysis_data.get('voice_analysis', {})
599
  voice_interpretation = generate_voice_interpretation(voice)
600
+ interviewee_responses = [f"- {u['text']}" for u in analysis_data['transcript'] if u.get('role') == 'Interviewee'][:5] or ["- No interviewee responses available."]
601
  acceptance_prob = analysis_data.get('acceptance_probability', 50.0)
602
  acceptance_line = f"\n**Suitability Score: {acceptance_prob:.2f}%**\n"
603
  if acceptance_prob >= 80:
 
609
  else:
610
  acceptance_line += "HR Verdict: Limited fit, significant improvement required."
611
 
612
+ # Robust prompt to ensure complete, specific content
613
  prompt = f"""
614
  You are EvalBot, a senior HR consultant delivering a comprehensive, professional interview analysis report.
615
+ Use clear section headings and bullet points, and concise, HR-focused language.
616
  Ensure text is clean, professional, and free of special characters that could break formatting.
617
  The interview involves two roles: Interviewer and Interviewee, assigned alternately to utterances.
618
+ If data is sparse, infer meaningful insights from metrics or responses.
619
  {acceptance_line}
620
  **1. Executive Summary**
621
+ - Provide 3 bullets summarizing the candidate's performance, strengths, and hiring potential.
622
  - Include metrics: Duration ({analysis_data['text_analysis']['total_duration']:.2f} seconds), Speaker Turns ({analysis_data['text_analysis']['speaker_turns']}).
623
  - Mention roles: Interviewer and Interviewee.
624
+ - **Example**: Strong leadership shown in project coordination; confidence score {voice.get('composite_scores', {}).get('confidence', 'N/A')}; ideal for team-oriented roles.
625
  **2. Communication and Vocal Dynamics**
626
+ - Evaluate vocal delivery (speaking rate: {voice.get('speaking_rate', 'N/A')} words/sec, filler words: {voice.get('filler_ratio', 0) * 100:.1f}%, confidence: {voice.get('interpretation', {}).get('confidence_level', 'N/A')}, anxiety: {voice.get('interpretation', {}).get('anxiety_level', 'N/A')}, fluency: {voice.get('interpretation', {}).get('fluency_level', 'N/A')}) in 3-4 bullets.
627
+ - Provide one actionable HR insight for workplace communication alignment.
628
  - Voice Analysis: {voice_interpretation}
629
  **3. Competency and Content**
630
+ - List 3-5 strengths (e.g., leadership, problem-solving, teamwork) with specific examples from responses or inferred from transcript.
631
+ - List 2-3 weaknesses (growth areas) with actionable feedback.
632
+ - Sample responses:
633
  {chr(10).join(interviewee_responses)}
634
  **4. Role Fit and Potential**
635
+ - Analyze cultural fit, role readiness, and growth potential in 3 bullets, inferred from strengths or metrics if needed.
636
+ - **Example**: Team collaboration skills align with dynamic cultures; high confidence suggests leadership potential.
637
  **5. Recommendations**
638
+ - Provide 4 specific recommendations for:
639
+ - Communication skills (e.g., clarity, reducing filler words).
640
+ - Content delivery (e.g., structured responses, key points).
641
+ - Professional presentation (e.g., confidence, tone).
642
+ - Include a strategy/example for each.
643
+ - Suggest 3 next steps for hiring managers (e.g., advance, train, assess).
644
  """
645
  response = gemini_model.generate_content(prompt)
 
646
  clean_text = re.sub(r'[^\x20-\x7E\n]+', '', response.text)
647
  return clean_text
648
  except Exception as e:
 
662
  styles = getSampleStyleSheet()
663
 
664
  # Custom styles
665
+ cover_title = ParagraphStyle(name='CoverTitle', fontSize=24, leading=28, spaceAfter=20, alignment=1, textColor=colors.HexColor('#003087'), fontName='Helvetica-Bold')
666
+ h1 = ParagraphStyle(name='Heading1', fontSize=16, leading=20, spaceAfter=14, alignment=1, textColor=colors.HexColor('#003087'), fontName='Helvetica-Bold')
667
+ h2 = ParagraphStyle(name='Heading2', fontSize=12, leading=15, spaceBefore=10, spaceAfter=8, textColor=colors.HexColor('#0050BC'), fontName='Helvetica-Bold')
668
+ h3 = ParagraphStyle(name='Heading3', fontSize=10, leading=12, spaceBefore=8, spaceAfter=6, textColor=colors.HexColor('#3F7CFF'), fontName='Helvetica-Bold')
669
+ body_text = ParagraphStyle(name='BodyText', fontSize=9, leading=12, spaceAfter=6, fontName='Helvetica', textColor=colors.HexColor('#333333'))
670
+ bullet_style = ParagraphStyle(name='Bullet', parent=body_text, leftIndent=18, bulletIndent=8, fontName='Helvetica', bulletFontName='Helvetica', bulletFontSize=9)
671
+ table_header = ParagraphStyle(name='TableHeader', fontSize=9, leading=11, textColor=colors.white, fontName='Helvetica-Bold')
672
+ table_body = ParagraphStyle(name='TableBody', fontSize=9, leading=11, fontName='Helvetica')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
 
674
  story = []
675
 
676
  def header_footer(canvas, doc):
677
  canvas.saveState()
 
678
  canvas.setFont('Helvetica', 8)
679
  canvas.setFillColor(colors.HexColor('#666666'))
680
  canvas.drawString(doc.leftMargin, 0.5*inch, f"Page {doc.page} | EvalBot HR Interview Report | Confidential")
681
  canvas.drawRightString(doc.width + doc.leftMargin, 0.5*inch, time.strftime('%B %d, %Y'))
 
682
  canvas.setStrokeColor(colors.HexColor('#0050BC'))
683
  canvas.setLineWidth(0.8)
684
  canvas.line(doc.leftMargin, doc.height + 0.9*inch, doc.width + doc.leftMargin, doc.height + 0.9*inch)
 
697
  story.append(Paragraph(f"Candidate ID: {analysis_data.get('user_id', 'N/A')}", body_text))
698
  story.append(Paragraph(f"Generated: {time.strftime('%B %d, %Y')}", body_text))
699
  story.append(Spacer(1, 0.5*inch))
700
+ story.append(Paragraph("Confidential", ParagraphStyle(name='Confidential', fontSize=10, alignment=1, textColor=colors.HexColor('#D32F2F'), fontName='Helvetica-Bold')))
701
+ story.append(PageBreak())
702
+
703
+ # Table of Contents
704
+ story.append(Paragraph("Table of Contents", h1))
705
+ toc_data = [
706
+ [Paragraph("Section", table_header), Paragraph("Page", table_header)],
707
+ [Paragraph("1. Interview Evaluation Summary", table_body), Paragraph("3", table_body)],
708
+ [Paragraph("2. Communication & Vocal Dynamics", table_body), Paragraph("4", table_body)],
709
+ [Paragraph("3. Executive Summary", table_body), Paragraph("4", table_body)],
710
+ [Paragraph("4. Competency & Evaluation", table_body), Paragraph("5", table_body)],
711
+ [Paragraph("5. Role Fit & Potential", table_body), Paragraph("5", table_body)],
712
+ [Paragraph("6. Recommendations", table_body), Paragraph("6", table_body)],
713
+ ]
714
+ toc_table = Table(toc_data, colWidths=[4*inch, 2*inch])
715
+ toc_table.setStyle(TableStyle([
716
+ ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#0050BC')),
717
+ ('TEXTCOLOR', (0,0), (-1,0), colors.white),
718
+ ('ALIGN', (0,0), (-1,-1), 'LEFT'),
719
+ ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
720
+ ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
721
+ ('FONTSIZE', (0,0), (-1,-1), 9),
722
+ ('BOTTOMPADDING', (0,0), (-1,-1), 6),
723
+ ('TOPPADING', (0,0), (-1,-1), 6),
724
+ ('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#DDE4EE')),
725
+ ]))
726
+ story.append(toc_table)
727
  story.append(PageBreak())
728
 
729
  # Title Page
730
  story.append(Paragraph("Interview Evaluation Summary", h1))
731
  story.append(Spacer(1, 0.3*inch))
 
732
  acceptance_prob = analysis_data.get('acceptance_probability', 50.0)
733
  prob_color = colors.HexColor('#2E7D32') if acceptance_prob >= 80 else (
734
  colors.HexColor('#F57C00') if acceptance_prob >= 60 else colors.HexColor('#D32F2F')
735
  )
736
  story.append(Paragraph(
737
  f"Suitability Score: <font size=14 color='{prob_color.hexval()}'><b>{acceptance_prob:.2f}%</b></font>",
738
+ ParagraphStyle(name='Score', fontSize=14, spaceAfter=12, alignment=1, fontName='Helvetica-Bold')
 
 
 
 
 
 
739
  ))
 
740
  if acceptance_prob >= 80:
741
  story.append(Paragraph("<b>HR Verdict:</b> Outstanding candidate, recommended for immediate advancement.", body_text))
742
  elif acceptance_prob >= 60:
 
747
  story.append(Paragraph("<b>HR Verdict:</b> Limited fit, significant improvement required.", body_text))
748
  story.append(Spacer(1, 0.2*inch))
749
 
 
750
  roles = sorted(set(u.get('role', 'Unknown') for u in analysis_data.get('transcript', [])))
751
  table_data = [
752
  [Paragraph('Metric', table_header), Paragraph('Value', table_header)],
 
762
  ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
763
  ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
764
  ('FONTSIZE', (0,0), (-1,-1), 9),
765
+ ('BOTTOMPADDING', (0,0), (-1,-1), 8),
766
+ ('TOPPADDING', (0,0), (-1,-1), 8),
767
  ('BACKGROUND', (0,1), (-1,-1), colors.HexColor('#F5F6FA')),
768
  ('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#DDE4EE')),
769
  ]))
 
776
  story.append(Paragraph("Detailed Candidate Evaluation", h1))
777
 
778
  # Communication and Vocal Dynamics
779
+ story.append(Paragraph("2. Communication & Vocal Dynamics", h2))
780
  voice_analysis = analysis_data.get('voice_analysis', {})
781
  if voice_analysis and 'error' not in voice_analysis:
782
  table_data = [
 
849
  if not clean_line: continue
850
  clean_line = re.sub(r'[()\[\]{}]', '', clean_line)
851
  if current_section == 'Competency':
852
+ if any(k in clean_line.lower() for k in ['leader', 'problem', 'commun', 'adapt', 'strength', 'effective', 'skill', 'team', 'project']):
853
  current_subsection = 'Strengths'
854
+ elif any(k in clean_line.lower() for k in ['improv', 'grow', 'weak', 'depth', 'challenge', 'gap']):
855
  current_subsection = 'Weaknesses'
856
  if current_subsection:
857
  sections[current_section][current_subsection].append(clean_line)
858
  elif current_section == 'Recommendations':
859
+ if any(k in clean_line.lower() for k in ['commun', 'tech', 'depth', 'pres', 'improve', 'enhance', 'clarity', 'structur', 'tone', 'deliver']):
860
  current_subsection = 'Development'
861
  elif any(k in clean_line.lower() for k in ['adv', 'train', 'assess', 'next', 'mentor', 'round']):
862
  current_subsection = 'Next Steps'
 
865
  else:
866
  sections[current_section].append(clean_line)
867
 
868
+ # Key Highlights
869
+ story.append(Paragraph("3. Key Highlights", h2))
870
  summary_data = [
871
  [Paragraph("Category", table_header), Paragraph("Detail", table_header)],
872
+ [Paragraph("Top Strength", table_body), Paragraph(sections['Competency']['Strengths'][0] if sections['Competency']['Strengths'] else "Demonstrated potential in leadership or teamwork.", table_body)],
873
+ [Paragraph("Key Weakness", table_body), Paragraph(sections['Competency']['Weaknesses'][0] if sections['Competency']['Weaknesses'] else "Needs improvement in response structure or technical skills.", table_body)],
874
+ [Paragraph("Top Recommendation", table_body), Paragraph(sections['Recommendations']['Development'][0] if sections['Recommendations']['Development'] else "Practice structured responses using the STAR method.", table_body)],
875
  ]
876
  summary_table = Table(summary_data, colWidths=[2*inch, 4*inch])
877
  summary_table.setStyle(TableStyle([
 
890
  story.append(Spacer(1, 0.3*inch))
891
 
892
  # Executive Summary
893
+ story.append(Paragraph("4. Executive Summary", h2))
894
  if sections['Executive Summary']:
895
  for line in sections['Executive Summary']:
896
  story.append(Paragraph(line, bullet_style))
897
  else:
898
+ summary_lines = [
899
+ f"High suitability score of {acceptance_prob:.2f}% indicates strong potential.",
900
+ f"Interview duration: {analysis_data['text_analysis']['total_duration']:.2f} seconds, {analysis_data['text_analysis']['speaker_turns']} speaker turns.",
901
+ "Strengths in leadership and teamwork; recommended for further evaluation."
902
+ ]
903
+ for line in summary_lines:
904
+ story.append(Paragraph(line, bullet_style))
905
  story.append(Spacer(1, 0.2*inch))
906
 
907
  # Competency and Content
908
+ story.append(Paragraph("5. Competency & Evaluation", h2))
909
  story.append(Paragraph("Strengths", h3))
910
  if sections['Competency']['Strengths']:
911
  strength_table = Table([[Paragraph(line, bullet_style)] for line in sections['Competency']['Strengths']], colWidths=[6*inch])
 
916
  ]))
917
  story.append(strength_table)
918
  else:
919
+ story.append(Paragraph("No specific strengths identified; candidate shows general potential in teamwork or initiative.", body_text))
920
  story.append(Spacer(1, 0.1*inch))
921
  story.append(Paragraph("Weaknesses", h3))
922
  if sections['Competency']['Weaknesses']:
 
928
  ]))
929
  story.append(weakness_table)
930
  else:
931
+ story.append(Paragraph("No specific weaknesses identified; focus on enhancing existing strengths.", body_text))
932
  story.append(Spacer(1, 0.2*inch))
933
 
934
  # Role Fit
935
+ story.append(Paragraph("6. Role Fit & Potential", h2))
936
  if sections['Role Fit']:
937
  for line in sections['Role Fit']:
938
  story.append(Paragraph(line, bullet_style))
939
  else:
940
+ fit_lines = [
941
+ f"Suitability score of {acceptance_prob:.2f}% suggests alignment with role requirements.",
942
+ "Strengths in collaboration indicate fit for team-oriented environments.",
943
+ "Further assessment needed to confirm long-term cultural fit."
944
+ ]
945
+ for line in fit_lines:
946
+ story.append(Paragraph(line, bullet_style))
947
  story.append(Spacer(1, 0.2*inch))
948
 
949
  # Recommendations
950
+ story.append(Paragraph("7. Recommendations", h2))
951
  story.append(Paragraph("Development Priorities", h3))
952
  if sections['Recommendations']['Development']:
953
  dev_table = Table([[Paragraph(line, bullet_style)] for line in sections['Recommendations']['Development']], colWidths=[6*inch])
 
958
  ]))
959
  story.append(dev_table)
960
  else:
961
+ dev_lines = [
962
+ "Improve communication clarity by practicing the STAR method for structured responses.",
963
+ "Enhance content delivery by quantifying achievements (e.g., 'Led a team to achieve 20% growth').",
964
+ "Boost professional presentation through public speaking workshops.",
965
+ "Reduce filler words via recorded practice sessions."
966
+ ]
967
+ dev_table = Table([[Paragraph(line, bullet_style)] for line in dev_lines], colWidths=[6*inch])
968
+ dev_table.setStyle(TableStyle([
969
+ ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E8F0FE')),
970
+ ('VALIGN', (0,0), (-1,-1), 'TOP'),
971
+ ('LEFTPADDING', (0,0), (-1,-1), 6),
972
+ ]))
973
+ story.append(dev_table)
974
  story.append(Spacer(1, 0.1*inch))
975
  story.append(Paragraph("Next Steps", h3))
976
  if sections['Recommendations']['Next Steps']:
977
  for line in sections['Recommendations']['Next Steps']:
978
  story.append(Paragraph(line, bullet_style))
979
  else:
980
+ next_steps = [
981
+ f"Advance to next round given {acceptance_prob:.2f}% suitability score.",
982
+ "Provide training to address technical or communication gaps.",
983
+ "Conduct a behavioral assessment to confirm role alignment."
984
+ ]
985
+ for line in next_steps:
986
+ story.append(Paragraph(line, bullet_style))
987
  story.append(Spacer(1, 0.2*inch))
988
 
989
  doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
 
994
  return False
995
 
996
 
 
997
  def convert_to_serializable(obj):
998
  if isinstance(obj, np.generic):
999
  return obj.item()