norhan12 commited on
Commit
2995e64
·
verified ·
1 Parent(s): 34523b7

Update process_interview.py

Browse files
Files changed (1) hide show
  1. process_interview.py +161 -153
process_interview.py CHANGED
@@ -645,26 +645,22 @@ Your task:
645
  - Fluency Level: {voice.get('interpretation', {}).get('fluency_level', 'N/A')}
646
  - Voice Interpretation Summary: {voice_interpretation}
647
  ### Report Sections to Generate:
648
- **1. Key Highlights**
649
- - Provide 3 bullets: top strength, most critical weakness, and most impactful recommendation.
650
- - Each bullet must be concise, specific, and derived from responses or metrics.
651
- - Example: "Top Strength: Demonstrated leadership by leading a project to 20% efficiency gain (Response: 'I led a team to...')."
652
- **2. Executive Summary**
653
  - 3 bullets summarizing performance, key strengths, and hiring recommendation.
654
  - Mention relevant metrics when applicable.
655
- **3. Communication and Vocal Dynamics**
656
  - Analyze delivery: speaking rate, filler words, confidence, anxiety, fluency.
657
  - Provide 3-4 insightful bullets.
658
  - Give 1 actionable improvement recommendation for workplace communication.
659
- **4. Competency and Content**
660
  - Identify 5-8 strengths (use HR competencies: leadership, teamwork, problem-solving, etc.).
661
  - For each: provide short explanation + concrete example inferred from responses.
662
  - Identify 5-10 weaknesses or development areas.
663
- - For each weakness: provide actionable, practical feedback furlongfeedback.
664
- **5. Role Fit and Potential**
665
  - Analyze role fit, cultural fit, growth potential in 3 bullets.
666
  - Use examples from responses whenever possible.
667
- **6. Recommendations**
668
  - Provide 5 actionable recommendations categorized into:
669
  - Communication Skills
670
  - Content Delivery
@@ -673,7 +669,7 @@ Your task:
673
  **Next Steps for Hiring Managers**
674
  - Provide 5 clear next steps: next round, training, assessment, mentorship, role fit review.
675
  Ensure each section is clearly titled exactly as requested above.
676
- Avoid repetition between sections, especially between Key Highlights and Executive Summary.
677
  Use professional HR tone.
678
  Begin the full analysis now.
679
  """
@@ -685,9 +681,7 @@ Begin the full analysis now.
685
  logger.error(f"Report generation failed: {str(e)}")
686
  return f"Error generating report: {str(e)}"
687
 
688
-
689
-
690
- def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text: str) -> bool:
691
  try:
692
  doc = SimpleDocTemplate(
693
  output_path,
@@ -698,7 +692,7 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
698
  bottomMargin=1*inch
699
  )
700
  styles = getSampleStyleSheet()
701
-
702
  # Custom styles
703
  cover_title = ParagraphStyle(name='CoverTitle', fontSize=24, leading=28, spaceAfter=20, alignment=1, textColor=colors.HexColor('#003087'), fontName='Helvetica-Bold')
704
  h1 = ParagraphStyle(name='Heading1', fontSize=16, leading=20, spaceAfter=14, alignment=1, textColor=colors.HexColor('#003087'), fontName='Helvetica-Bold')
@@ -708,7 +702,7 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
708
  bullet_style = ParagraphStyle(name='Bullet', parent=body_text, leftIndent=18, bulletIndent=8, fontName='Helvetica', bulletFontName='Helvetica', bulletFontSize=9)
709
  table_header = ParagraphStyle(name='TableHeader', fontSize=9, leading=11, textColor=colors.white, fontName='Helvetica-Bold')
710
  table_body = ParagraphStyle(name='TableBody', fontSize=9, leading=11, fontName='Helvetica')
711
-
712
  story = []
713
 
714
  def header_footer(canvas, doc):
@@ -738,73 +732,16 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
738
  story.append(Paragraph("Confidential", ParagraphStyle(name='Confidential', fontSize=10, alignment=1, textColor=colors.HexColor('#D32F2F'), fontName='Helvetica-Bold')))
739
  story.append(PageBreak())
740
 
741
- # Parse gemini_report_text with robust section matching
742
- sections = {
743
- "Key Highlights": [],
744
- "Executive Summary": [],
745
- "Communication and Vocal Dynamics": [],
746
- "Competency and Content": {"Strengths": [], "Weaknesses": []},
747
- "Role Fit and Potential": [],
748
- "Recommendations": {"Development": [], "Next Steps": []},
749
- }
750
- current_section = None
751
- current_subsection = None
752
- lines = gemini_report_text.split('\n')
753
- for line in lines:
754
- line = line.strip()
755
- if not line:
756
- continue
757
- # Match section headers exactly
758
- section_headers = [
759
- "Key Highlights",
760
- "Executive Summary",
761
- "Communication and Vocal Dynamics",
762
- "Competency and Content",
763
- "Role Fit and Potential",
764
- "Recommendations"
765
- ]
766
- if line in section_headers:
767
- current_section = line
768
- current_subsection = None
769
- continue
770
- # Handle subsections for Competency and Content
771
- if current_section == "Competency and Content":
772
- if line.lower().startswith("strengths"):
773
- current_subsection = "Strengths"
774
- continue
775
- elif line.lower().startswith("weaknesses"):
776
- current_subsection = "Weaknesses"
777
- continue
778
- # Handle subsections for Recommendations
779
- if current_section == "Recommendations":
780
- if line.lower().startswith("communication skills") or line.lower().startswith("content delivery") or line.lower().startswith("professional presentation"):
781
- current_subsection = "Development"
782
- continue
783
- elif line.lower().startswith("next steps for hiring managers"):
784
- current_subsection = "Next Steps"
785
- continue
786
- # Add bullet points to the appropriate section
787
- if re.match(r'^[-*•]\s+', line) and current_section:
788
- clean_line = re.sub(r'^[-*•]\s+', '', line).strip()
789
- if not clean_line:
790
- continue
791
- clean_line = re.sub(r'[()\[\]{}]', '', clean_line)
792
- if current_section in ["Competency and Content", "Recommendations"] and current_subsection:
793
- sections[current_section][current_subsection].append(clean_line)
794
- else:
795
- sections[current_section].append(clean_line)
796
-
797
  # Table of Contents
798
  story.append(Paragraph("Table of Contents", h1))
799
  toc_data = [
800
  [Paragraph("Section", table_header), Paragraph("Page", table_header)],
801
  [Paragraph("1. Interview Evaluation Summary", table_body), Paragraph("3", table_body)],
802
- [Paragraph("2. Key Highlights", table_body), Paragraph("4", table_body)],
803
- [Paragraph("3. Communication and Vocal Dynamics", table_body), Paragraph("4", table_body)],
804
- [Paragraph("4. Executive Summary", table_body), Paragraph("5", table_body)],
805
- [Paragraph("5. Competency and Content", table_body), Paragraph("5", table_body)],
806
- [Paragraph("6. Role Fit and Potential", table_body), Paragraph("6", table_body)],
807
- [Paragraph("7. Recommendations", table_body), Paragraph("6", table_body)],
808
  ]
809
  toc_table = Table(toc_data, colWidths=[4*inch, 2*inch])
810
  toc_table.setStyle(TableStyle([
@@ -815,13 +752,13 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
815
  ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
816
  ('FONTSIZE', (0,0), (-1,-1), 9),
817
  ('BOTTOMPADDING', (0,0), (-1,-1), 6),
818
- ('TOPPADDING', (0,0), (-1,-1), 6),
819
  ('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#DDE4EE')),
820
  ]))
821
  story.append(toc_table)
822
  story.append(PageBreak())
823
 
824
- # Interview Evaluation Summary
825
  story.append(Paragraph("Interview Evaluation Summary", h1))
826
  story.append(Spacer(1, 0.3*inch))
827
  acceptance_prob = analysis_data.get('acceptance_probability', 50.0)
@@ -841,17 +778,13 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
841
  else:
842
  story.append(Paragraph("<b>HR Verdict:</b> Limited fit, significant improvement required.", body_text))
843
  story.append(Spacer(1, 0.2*inch))
844
-
845
- # Metrics Table
846
- voice_analysis = analysis_data.get('voice_analysis', {})
847
  roles = sorted(set(u.get('role', 'Unknown') for u in analysis_data.get('transcript', [])))
848
  table_data = [
849
  [Paragraph('Metric', table_header), Paragraph('Value', table_header)],
850
- [Paragraph('Interview Duration', table_body), Paragraph(f"{analysis_data.get('text_analysis', {}).get('total_duration', 0):.2f} seconds", table_body)],
851
- [Paragraph('Speaker Turns', table_body), Paragraph(f"{analysis_data.get('text_analysis', {}).get('speaker_turns', 0)}", table_body)],
852
- [Paragraph('Roles', table_body), Paragraph(', '.join(roles) if roles else 'N/A', table_body)],
853
- [Paragraph('Speaking Rate', table_body), Paragraph(f"{voice_analysis.get('speaking_rate', 'N/A')} words/sec", table_body)],
854
- [Paragraph('Filler Words', table_body), Paragraph(f"{voice_analysis.get('filler_ratio', 0) * 100:.1f}%", table_body)],
855
  ]
856
  table = Table(table_data, colWidths=[2.3*inch, 3.7*inch])
857
  table.setStyle(TableStyle([
@@ -873,34 +806,10 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
873
 
874
  # Detailed Analysis
875
  story.append(Paragraph("Detailed Candidate Evaluation", h1))
876
-
877
- # Key Highlights
878
- story.append(Paragraph("Key Highlights", h2))
879
- if sections["Key Highlights"]:
880
- highlight_table = Table([[Paragraph(line, bullet_style)] for line in sections["Key Highlights"]], colWidths=[6*inch])
881
- highlight_table.setStyle(TableStyle([
882
- ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E8F0FE')),
883
- ('VALIGN', (0,0), (-1,-1), 'TOP'),
884
- ('LEFTPADDING', (0,0), (-1,-1), 6),
885
- ]))
886
- story.append(highlight_table)
887
- else:
888
- fallback_highlights = [
889
- f"Top Strength: Demonstrated potential based on {acceptance_prob:.2f}% suitability score.",
890
- "Critical Weakness: Further clarity needed in responses (review transcript for details).",
891
- "Key Recommendation: Practice structured responses to enhance communication."
892
- ]
893
- highlight_table = Table([[Paragraph(line, bullet_style)] for line in fallback_highlights], colWidths=[6*inch])
894
- highlight_table.setStyle(TableStyle([
895
- ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E8F0FE')),
896
- ('VALIGN', (0,0), (-1,-1), 'TOP'),
897
- ('LEFTPADDING', (0,0), (-1,-1), 6),
898
- ]))
899
- story.append(highlight_table)
900
- story.append(Spacer(1, 0.2*inch))
901
-
902
  # Communication and Vocal Dynamics
903
- story.append(Paragraph("Communication and Vocal Dynamics", h2))
 
904
  if voice_analysis and 'error' not in voice_analysis:
905
  table_data = [
906
  [Paragraph('Metric', table_header), Paragraph('Value', table_header), Paragraph('HR Insight', table_header)],
@@ -925,37 +834,113 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
925
  ]))
926
  story.append(table)
927
  story.append(Spacer(1, 0.2*inch))
928
- try:
929
- chart_buffer = io.BytesIO()
930
- generate_anxiety_confidence_chart(voice_analysis.get('composite_scores', {}), chart_buffer)
931
- chart_buffer.seek(0)
932
- img = Image(chart_buffer, width=4.5*inch, height=3*inch)
933
- img.hAlign = 'CENTER'
934
- story.append(img)
935
- except Exception as e:
936
- logger.warning(f"Failed to generate chart: {str(e)}")
937
- story.append(Paragraph("Chart unavailable due to missing or invalid data.", body_text))
938
  else:
939
- error_msg = voice_analysis.get('error', 'No voice analysis data available')
940
- story.append(Paragraph(f"Vocal analysis unavailable: {error_msg}", body_text))
941
- for line in sections["Communication and Vocal Dynamics"]:
942
- story.append(Paragraph(line, bullet_style))
943
  story.append(Spacer(1, 0.2*inch))
944
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
945
  # Executive Summary
946
- story.append(Paragraph("Executive Summary", h2))
947
- if sections["Executive Summary"]:
948
- for line in sections["Executive Summary"]:
949
  story.append(Paragraph(line, bullet_style))
950
  else:
951
- story.append(Paragraph("No executive summary provided; refer to metrics for candidate performance.", body_text))
 
 
 
 
 
 
952
  story.append(Spacer(1, 0.2*inch))
953
 
954
  # Competency and Content
955
- story.append(Paragraph("Competency and Content", h2))
956
  story.append(Paragraph("Strengths", h3))
957
- if sections["Competency and Content"]["Strengths"]:
958
- strength_table = Table([[Paragraph(line, bullet_style)] for line in sections["Competency and Content"]["Strengths"]], colWidths=[6*inch])
959
  strength_table.setStyle(TableStyle([
960
  ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E6FFE6')),
961
  ('VALIGN', (0,0), (-1,-1), 'TOP'),
@@ -963,11 +948,11 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
963
  ]))
964
  story.append(strength_table)
965
  else:
966
- story.append(Paragraph("No specific strengths identified; candidate shows general potential.", body_text))
967
  story.append(Spacer(1, 0.1*inch))
968
  story.append(Paragraph("Weaknesses", h3))
969
- if sections["Competency and Content"]["Weaknesses"]:
970
- weakness_table = Table([[Paragraph(line, bullet_style)] for line in sections["Competency and Content"]["Weaknesses"]], colWidths=[6*inch])
971
  weakness_table.setStyle(TableStyle([
972
  ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#FFF0F0')),
973
  ('VALIGN', (0,0), (-1,-1), 'TOP'),
@@ -978,20 +963,26 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
978
  story.append(Paragraph("No specific weaknesses identified; focus on enhancing existing strengths.", body_text))
979
  story.append(Spacer(1, 0.2*inch))
980
 
981
- # Role Fit and Potential
982
- story.append(Paragraph("Role Fit and Potential", h2))
983
- if sections["Role Fit and Potential"]:
984
- for line in sections["Role Fit and Potential"]:
985
  story.append(Paragraph(line, bullet_style))
986
  else:
987
- story.append(Paragraph("No role fit analysis provided; assess based on metrics and responses.", body_text))
 
 
 
 
 
 
988
  story.append(Spacer(1, 0.2*inch))
989
 
990
  # Recommendations
991
- story.append(Paragraph("Recommendations", h2))
992
  story.append(Paragraph("Development Priorities", h3))
993
- if sections["Recommendations"]["Development"]:
994
- dev_table = Table([[Paragraph(line, bullet_style)] for line in sections["Recommendations"]["Development"]], colWidths=[6*inch])
995
  dev_table.setStyle(TableStyle([
996
  ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E8F0FE')),
997
  ('VALIGN', (0,0), (-1,-1), 'TOP'),
@@ -999,17 +990,34 @@ def create_pdf_report(analysis_data: dict, output_path: str, gemini_report_text:
999
  ]))
1000
  story.append(dev_table)
1001
  else:
1002
- story.append(Paragraph("No development recommendations provided; focus on general skill enhancement.", body_text))
 
 
 
 
 
 
 
 
 
 
 
 
1003
  story.append(Spacer(1, 0.1*inch))
1004
- story.append(Paragraph("Next Steps for Hiring Managers", h3))
1005
- if sections["Recommendations"]["Next Steps"]:
1006
- for line in sections["Recommendations"]["Next Steps"]:
1007
  story.append(Paragraph(line, bullet_style))
1008
  else:
1009
- story.append(Paragraph("No next steps provided; consider further evaluation based on suitability score.", body_text))
 
 
 
 
 
 
1010
  story.append(Spacer(1, 0.2*inch))
1011
 
1012
- # Build PDF
1013
  doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
1014
  logger.info(f"PDF report successfully generated at {output_path}")
1015
  return True
 
645
  - Fluency Level: {voice.get('interpretation', {}).get('fluency_level', 'N/A')}
646
  - Voice Interpretation Summary: {voice_interpretation}
647
  ### Report Sections to Generate:
648
+ **1. Executive Summary**
 
 
 
 
649
  - 3 bullets summarizing performance, key strengths, and hiring recommendation.
650
  - Mention relevant metrics when applicable.
651
+ **2. Communication and Vocal Dynamics**
652
  - Analyze delivery: speaking rate, filler words, confidence, anxiety, fluency.
653
  - Provide 3-4 insightful bullets.
654
  - Give 1 actionable improvement recommendation for workplace communication.
655
+ **3. Competency and Content**
656
  - Identify 5-8 strengths (use HR competencies: leadership, teamwork, problem-solving, etc.).
657
  - For each: provide short explanation + concrete example inferred from responses.
658
  - Identify 5-10 weaknesses or development areas.
659
+ - For each weakness: provide actionable, practical feedback.
660
+ **4. Role Fit and Potential**
661
  - Analyze role fit, cultural fit, growth potential in 3 bullets.
662
  - Use examples from responses whenever possible.
663
+ **5. Recommendations**
664
  - Provide 5 actionable recommendations categorized into:
665
  - Communication Skills
666
  - Content Delivery
 
669
  **Next Steps for Hiring Managers**
670
  - Provide 5 clear next steps: next round, training, assessment, mentorship, role fit review.
671
  Ensure each section is clearly titled exactly as requested above.
672
+ Avoid repetition between sections.
673
  Use professional HR tone.
674
  Begin the full analysis now.
675
  """
 
681
  logger.error(f"Report generation failed: {str(e)}")
682
  return f"Error generating report: {str(e)}"
683
 
684
+ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text: str) -> bool:
 
 
685
  try:
686
  doc = SimpleDocTemplate(
687
  output_path,
 
692
  bottomMargin=1*inch
693
  )
694
  styles = getSampleStyleSheet()
695
+
696
  # Custom styles
697
  cover_title = ParagraphStyle(name='CoverTitle', fontSize=24, leading=28, spaceAfter=20, alignment=1, textColor=colors.HexColor('#003087'), fontName='Helvetica-Bold')
698
  h1 = ParagraphStyle(name='Heading1', fontSize=16, leading=20, spaceAfter=14, alignment=1, textColor=colors.HexColor('#003087'), fontName='Helvetica-Bold')
 
702
  bullet_style = ParagraphStyle(name='Bullet', parent=body_text, leftIndent=18, bulletIndent=8, fontName='Helvetica', bulletFontName='Helvetica', bulletFontSize=9)
703
  table_header = ParagraphStyle(name='TableHeader', fontSize=9, leading=11, textColor=colors.white, fontName='Helvetica-Bold')
704
  table_body = ParagraphStyle(name='TableBody', fontSize=9, leading=11, fontName='Helvetica')
705
+
706
  story = []
707
 
708
  def header_footer(canvas, doc):
 
732
  story.append(Paragraph("Confidential", ParagraphStyle(name='Confidential', fontSize=10, alignment=1, textColor=colors.HexColor('#D32F2F'), fontName='Helvetica-Bold')))
733
  story.append(PageBreak())
734
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
735
  # Table of Contents
736
  story.append(Paragraph("Table of Contents", h1))
737
  toc_data = [
738
  [Paragraph("Section", table_header), Paragraph("Page", table_header)],
739
  [Paragraph("1. Interview Evaluation Summary", table_body), Paragraph("3", table_body)],
740
+ [Paragraph("2. Communication & Vocal Dynamics", table_body), Paragraph("4", table_body)],
741
+ [Paragraph("3. Executive Summary", table_body), Paragraph("4", table_body)],
742
+ [Paragraph("4. Competency & Evaluation", table_body), Paragraph("5", table_body)],
743
+ [Paragraph("5. Role Fit & Potential", table_body), Paragraph("5", table_body)],
744
+ [Paragraph("6. Recommendations", table_body), Paragraph("6", table_body)],
 
745
  ]
746
  toc_table = Table(toc_data, colWidths=[4*inch, 2*inch])
747
  toc_table.setStyle(TableStyle([
 
752
  ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
753
  ('FONTSIZE', (0,0), (-1,-1), 9),
754
  ('BOTTOMPADDING', (0,0), (-1,-1), 6),
755
+ ('TOPPADING', (0,0), (-1,-1), 6),
756
  ('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#DDE4EE')),
757
  ]))
758
  story.append(toc_table)
759
  story.append(PageBreak())
760
 
761
+ # Title Page
762
  story.append(Paragraph("Interview Evaluation Summary", h1))
763
  story.append(Spacer(1, 0.3*inch))
764
  acceptance_prob = analysis_data.get('acceptance_probability', 50.0)
 
778
  else:
779
  story.append(Paragraph("<b>HR Verdict:</b> Limited fit, significant improvement required.", body_text))
780
  story.append(Spacer(1, 0.2*inch))
781
+
 
 
782
  roles = sorted(set(u.get('role', 'Unknown') for u in analysis_data.get('transcript', [])))
783
  table_data = [
784
  [Paragraph('Metric', table_header), Paragraph('Value', table_header)],
785
+ [Paragraph('Interview Duration', table_body), Paragraph(f"{analysis_data['text_analysis'].get('total_duration', 0):.2f} seconds", table_body)],
786
+ [Paragraph('Speaker Turns', table_body), Paragraph(f"{analysis_data['text_analysis'].get('speaker_turns', 0)}", table_body)],
787
+ [Paragraph('Roles', table_body), Paragraph(', '.join(roles), table_body)],
 
 
788
  ]
789
  table = Table(table_data, colWidths=[2.3*inch, 3.7*inch])
790
  table.setStyle(TableStyle([
 
806
 
807
  # Detailed Analysis
808
  story.append(Paragraph("Detailed Candidate Evaluation", h1))
809
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
810
  # Communication and Vocal Dynamics
811
+ story.append(Paragraph("2. Communication & Vocal Dynamics", h2))
812
+ voice_analysis = analysis_data.get('voice_analysis', {})
813
  if voice_analysis and 'error' not in voice_analysis:
814
  table_data = [
815
  [Paragraph('Metric', table_header), Paragraph('Value', table_header), Paragraph('HR Insight', table_header)],
 
834
  ]))
835
  story.append(table)
836
  story.append(Spacer(1, 0.2*inch))
837
+ chart_buffer = io.BytesIO()
838
+ generate_anxiety_confidence_chart(voice_analysis.get('composite_scores', {}), chart_buffer)
839
+ chart_buffer.seek(0)
840
+ img = Image(chart_buffer, width=4.5*inch, height=3*inch)
841
+ img.hAlign = 'CENTER'
842
+ story.append(img)
 
 
 
 
843
  else:
844
+ story.append(Paragraph(f"Vocal analysis unavailable: {voice_analysis.get('error', 'No data available')}", body_text))
 
 
 
845
  story.append(Spacer(1, 0.2*inch))
846
 
847
+ # Parse Gemini Report
848
+ sections = {
849
+ "Executive Summary": [],
850
+ "Communication": [],
851
+ "Competency": {"Strengths": [], "Weaknesses": []},
852
+ "Role Fit": [],
853
+ "Recommendations": {"Development": [], "Next Steps": []},
854
+ }
855
+ current_section = None
856
+ current_subsection = None
857
+ lines = gemini_report_text.split('\n')
858
+ for line in lines:
859
+ line = line.strip()
860
+ if not line: continue
861
+ heading_match = re.match(r'^\**(\d+\.\s+)?([^\*]+)\**$', line)
862
+ if heading_match:
863
+ section_title = heading_match.group(2).strip()
864
+ if 'Executive Summary' in section_title:
865
+ current_section = 'Executive Summary'
866
+ current_subsection = None
867
+ elif 'Communication' in section_title:
868
+ current_section = 'Communication'
869
+ current_subsection = None
870
+ elif 'Competency' in section_title:
871
+ current_section = 'Competency'
872
+ current_subsection = None
873
+ elif 'Role Fit' in section_title:
874
+ current_section = 'Role Fit'
875
+ current_subsection = None
876
+ elif 'Recommendations' in section_title:
877
+ current_section = 'Recommendations'
878
+ current_subsection = None
879
+ elif re.match(r'^[-*•]\s+', line) and current_section:
880
+ clean_line = re.sub(r'^[-*•]\s+', '', line).strip()
881
+ if not clean_line: continue
882
+ clean_line = re.sub(r'[()\[\]{}]', '', clean_line)
883
+ if current_section == 'Competency':
884
+ if any(k in clean_line.lower() for k in ['leader', 'problem', 'commun', 'adapt', 'strength', 'effective', 'skill', 'team', 'project']):
885
+ current_subsection = 'Strengths'
886
+ elif any(k in clean_line.lower() for k in ['improv', 'grow', 'weak', 'depth', 'challenge', 'gap']):
887
+ current_subsection = 'Weaknesses'
888
+ if current_subsection:
889
+ sections[current_section][current_subsection].append(clean_line)
890
+ elif current_section == 'Recommendations':
891
+ if any(k in clean_line.lower() for k in ['commun', 'tech', 'depth', 'pres', 'improve', 'enhance', 'clarity', 'structur', 'tone', 'deliver']):
892
+ current_subsection = 'Development'
893
+ elif any(k in clean_line.lower() for k in ['adv', 'train', 'assess', 'next', 'mentor', 'round']):
894
+ current_subsection = 'Next Steps'
895
+ if current_subsection:
896
+ sections[current_section][current_subsection].append(clean_line)
897
+ else:
898
+ sections[current_section].append(clean_line)
899
+
900
+ # Key Highlights
901
+ story.append(Paragraph("3. Key Highlights", h2))
902
+ summary_data = [
903
+ [Paragraph("Category", table_header), Paragraph("Detail", table_header)],
904
+ [Paragraph("Top Strength", table_body), Paragraph(sections['Competency']['Strengths'][0] if sections['Competency']['Strengths'] else "Demonstrated potential in leadership or teamwork.", table_body)],
905
+ [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)],
906
+ [Paragraph("Top Recommendation", table_body), Paragraph(sections['Recommendations']['Development'][0] if sections['Recommendations']['Development'] else "Practice structured responses using the STAR method.", table_body)],
907
+ ]
908
+ summary_table = Table(summary_data, colWidths=[2*inch, 4*inch])
909
+ summary_table.setStyle(TableStyle([
910
+ ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#0050BC')),
911
+ ('TEXTCOLOR', (0,0), (-1,0), colors.white),
912
+ ('ALIGN', (0,0), (-1,-1), 'LEFT'),
913
+ ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
914
+ ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
915
+ ('FONTSIZE', (0,0), (-1,-1), 9),
916
+ ('BOTTOMPADDING', (0,0), (-1,-1), 6),
917
+ ('TOPPADDING', (0,0), (-1,-1), 6),
918
+ ('BACKGROUND', (0,1), (-1,-1), colors.HexColor('#E8F0FE')),
919
+ ('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#DDE4EE')),
920
+ ]))
921
+ story.append(summary_table)
922
+ story.append(Spacer(1, 0.3*inch))
923
+
924
  # Executive Summary
925
+ story.append(Paragraph("4. Executive Summary", h2))
926
+ if sections['Executive Summary']:
927
+ for line in sections['Executive Summary']:
928
  story.append(Paragraph(line, bullet_style))
929
  else:
930
+ summary_lines = [
931
+ f"High suitability score of {acceptance_prob:.2f}% indicates strong potential.",
932
+ f"Interview duration: {analysis_data['text_analysis']['total_duration']:.2f} seconds, {analysis_data['text_analysis']['speaker_turns']} speaker turns.",
933
+ "Strengths in leadership and teamwork; recommended for further evaluation."
934
+ ]
935
+ for line in summary_lines:
936
+ story.append(Paragraph(line, bullet_style))
937
  story.append(Spacer(1, 0.2*inch))
938
 
939
  # Competency and Content
940
+ story.append(Paragraph("5. Competency & Evaluation", h2))
941
  story.append(Paragraph("Strengths", h3))
942
+ if sections['Competency']['Strengths']:
943
+ strength_table = Table([[Paragraph(line, bullet_style)] for line in sections['Competency']['Strengths']], colWidths=[6*inch])
944
  strength_table.setStyle(TableStyle([
945
  ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E6FFE6')),
946
  ('VALIGN', (0,0), (-1,-1), 'TOP'),
 
948
  ]))
949
  story.append(strength_table)
950
  else:
951
+ story.append(Paragraph("No specific strengths identified; candidate shows general potential in teamwork or initiative.", body_text))
952
  story.append(Spacer(1, 0.1*inch))
953
  story.append(Paragraph("Weaknesses", h3))
954
+ if sections['Competency']['Weaknesses']:
955
+ weakness_table = Table([[Paragraph(line, bullet_style)] for line in sections['Competency']['Weaknesses']], colWidths=[6*inch])
956
  weakness_table.setStyle(TableStyle([
957
  ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#FFF0F0')),
958
  ('VALIGN', (0,0), (-1,-1), 'TOP'),
 
963
  story.append(Paragraph("No specific weaknesses identified; focus on enhancing existing strengths.", body_text))
964
  story.append(Spacer(1, 0.2*inch))
965
 
966
+ # Role Fit
967
+ story.append(Paragraph("6. Role Fit & Potential", h2))
968
+ if sections['Role Fit']:
969
+ for line in sections['Role Fit']:
970
  story.append(Paragraph(line, bullet_style))
971
  else:
972
+ fit_lines = [
973
+ f"Suitability score of {acceptance_prob:.2f}% suggests alignment with role requirements.",
974
+ "Strengths in collaboration indicate fit for team-oriented environments.",
975
+ "Further assessment needed to confirm long-term cultural fit."
976
+ ]
977
+ for line in fit_lines:
978
+ story.append(Paragraph(line, bullet_style))
979
  story.append(Spacer(1, 0.2*inch))
980
 
981
  # Recommendations
982
+ story.append(Paragraph("7. Recommendations", h2))
983
  story.append(Paragraph("Development Priorities", h3))
984
+ if sections['Recommendations']['Development']:
985
+ dev_table = Table([[Paragraph(line, bullet_style)] for line in sections['Recommendations']['Development']], colWidths=[6*inch])
986
  dev_table.setStyle(TableStyle([
987
  ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E8F0FE')),
988
  ('VALIGN', (0,0), (-1,-1), 'TOP'),
 
990
  ]))
991
  story.append(dev_table)
992
  else:
993
+ dev_lines = [
994
+ "Improve communication clarity by practicing the STAR method for structured responses.",
995
+ "Enhance content delivery by quantifying achievements (e.g., 'Led a team to achieve 20% growth').",
996
+ "Boost professional presentation through public speaking workshops.",
997
+ "Reduce filler words via recorded practice sessions."
998
+ ]
999
+ dev_table = Table([[Paragraph(line, bullet_style)] for line in dev_lines], colWidths=[6*inch])
1000
+ dev_table.setStyle(TableStyle([
1001
+ ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#E8F0FE')),
1002
+ ('VALIGN', (0,0), (-1,-1), 'TOP'),
1003
+ ('LEFTPADDING', (0,0), (-1,-1), 6),
1004
+ ]))
1005
+ story.append(dev_table)
1006
  story.append(Spacer(1, 0.1*inch))
1007
+ story.append(Paragraph("Next Steps", h3))
1008
+ if sections['Recommendations']['Next Steps']:
1009
+ for line in sections['Recommendations']['Next Steps']:
1010
  story.append(Paragraph(line, bullet_style))
1011
  else:
1012
+ next_steps = [
1013
+ f"Advance to next round given {acceptance_prob:.2f}% suitability score.",
1014
+ "Provide training to address technical or communication gaps.",
1015
+ "Conduct a behavioral assessment to confirm role alignment."
1016
+ ]
1017
+ for line in next_steps:
1018
+ story.append(Paragraph(line, bullet_style))
1019
  story.append(Spacer(1, 0.2*inch))
1020
 
 
1021
  doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
1022
  logger.info(f"PDF report successfully generated at {output_path}")
1023
  return True