Update process_interview.py
Browse files- process_interview.py +33 -20
process_interview.py
CHANGED
|
@@ -391,7 +391,7 @@ def generate_voice_interpretation(analysis: Dict) -> str:
|
|
| 391 |
if 'error' in analysis:
|
| 392 |
return f"Voice analysis unavailable: {analysis['error']}"
|
| 393 |
interpretation_lines = [
|
| 394 |
-
f"- Speaking rate: {analysis.get('speaking_rate', 0):.2f} words/sec (Benchmark: 2.0-3.0; affects clarity)",
|
| 395 |
f"- Filler words: {analysis.get('filler_ratio', 0) * 100:.1f}% (High usage reduces credibility)",
|
| 396 |
f"- Anxiety: {analysis.get('interpretation', {}).get('anxiety_level', 'N/A')} (Score: {analysis.get('composite_scores', {}).get('anxiety', 0):.3f}; stress response)",
|
| 397 |
f"- Confidence: {analysis.get('interpretation', {}).get('confidence_level', 'N/A')} (Score: {analysis.get('composite_scores', {}).get('confidence', 0):.3f}; vocal strength)",
|
|
@@ -568,7 +568,7 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 568 |
canvas.saveState()
|
| 569 |
canvas.setFont('Helvetica', 7)
|
| 570 |
canvas.setFillColor(colors.HexColor('#666666'))
|
| 571 |
-
canvas.drawString(doc.leftMargin, 0.5*inch, f"Page {doc.page} | EvalBot HR Interview Report | Confidential")
|
| 572 |
canvas.setStrokeColor(colors.HexColor('#0050BC'))
|
| 573 |
canvas.setLineWidth(0.5)
|
| 574 |
canvas.line(doc.leftMargin, doc.height + 0.9*inch, doc.width + doc.leftMargin, doc.height + 0.9*inch)
|
|
@@ -678,6 +678,7 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 678 |
line = line.strip()
|
| 679 |
if not line:
|
| 680 |
continue
|
|
|
|
| 681 |
if line.startswith('**') and line.endswith('**'):
|
| 682 |
section_title = line.strip('**').strip()
|
| 683 |
if section_title.startswith(('1.', '2.', '3.', '4.', '5.')):
|
|
@@ -697,30 +698,42 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 697 |
elif 'Recommendations' in section_title:
|
| 698 |
current_section = 'Recommendations'
|
| 699 |
current_subsection = None
|
|
|
|
| 700 |
elif line.startswith('-') and current_section:
|
| 701 |
clean_line = line.lstrip('-').strip()
|
| 702 |
if not clean_line:
|
| 703 |
continue
|
| 704 |
clean_line = re.sub(r'[^\w\s.,;:-]', '', clean_line)
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
elif any(k in clean_line.lower() for k in ['adv', 'assess', 'next', 'schedule', 'mentor']):
|
| 716 |
-
current_subsection = 'Next Steps'
|
| 717 |
if current_subsection:
|
| 718 |
sections[current_section][current_subsection].append(clean_line)
|
|
|
|
|
|
|
| 719 |
else:
|
|
|
|
| 720 |
sections[current_section].append(clean_line)
|
| 721 |
elif current_section and line:
|
| 722 |
clean_line = re.sub(r'[^\w\s.,;:-]', '', line)
|
| 723 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 724 |
|
| 725 |
# Executive Summary
|
| 726 |
story.append(Paragraph("2. Executive Summary", h2))
|
|
@@ -728,7 +741,7 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 728 |
for line in sections['Executive Summary']:
|
| 729 |
story.append(Paragraph(line, bullet_style))
|
| 730 |
else:
|
| 731 |
-
story.append(Paragraph("Candidate showed moderate engagement; further
|
| 732 |
story.append(Paragraph(f"Interview lasted {analysis_data['text_analysis']['total_duration']:.2f} seconds with {analysis_data['text_analysis']['speaker_turns']} turns.", bullet_style))
|
| 733 |
story.append(Spacer(1, 0.15*inch))
|
| 734 |
|
|
@@ -755,15 +768,15 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 755 |
for line in sections['Role Fit']:
|
| 756 |
story.append(Paragraph(line, bullet_style))
|
| 757 |
else:
|
| 758 |
-
story.append(Paragraph("Potential for role fit exists; further evaluation needed to confirm alignment.", bullet_style))
|
| 759 |
story.append(Spacer(1, 0.15*inch))
|
| 760 |
-
|
| 761 |
# Recommendations
|
| 762 |
story.append(Paragraph("5. Recommendations", h2))
|
| 763 |
story.append(Paragraph("Development Priorities", h3))
|
| 764 |
if sections['Recommendations']['Development']:
|
| 765 |
for line in sections['Recommendations']['Development']:
|
| 766 |
-
story.append(Paragraph(line, bullet_style))
|
| 767 |
else:
|
| 768 |
story.append(Paragraph("Enroll in communication training to reduce filler words.", bullet_style))
|
| 769 |
story.append(Spacer(1, 0.1*inch))
|
|
@@ -780,7 +793,7 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 780 |
logger.info(f"PDF report successfully generated at {output_path}")
|
| 781 |
return True
|
| 782 |
except Exception as e:
|
| 783 |
-
logger.error(f"PDF generation failed: {str(e)}", exc_info=True)
|
| 784 |
return False
|
| 785 |
|
| 786 |
def convert_to_serializable(obj):
|
|
|
|
| 391 |
if 'error' in analysis:
|
| 392 |
return f"Voice analysis unavailable: {analysis['error']}"
|
| 393 |
interpretation_lines = [
|
| 394 |
+
f"- Speaking rate: {analysis.get('speaking_rate', 0):.2f} words/sec (Benchmark: 2.0-3.0; affects clarity)",
|
| 395 |
f"- Filler words: {analysis.get('filler_ratio', 0) * 100:.1f}% (High usage reduces credibility)",
|
| 396 |
f"- Anxiety: {analysis.get('interpretation', {}).get('anxiety_level', 'N/A')} (Score: {analysis.get('composite_scores', {}).get('anxiety', 0):.3f}; stress response)",
|
| 397 |
f"- Confidence: {analysis.get('interpretation', {}).get('confidence_level', 'N/A')} (Score: {analysis.get('composite_scores', {}).get('confidence', 0):.3f}; vocal strength)",
|
|
|
|
| 568 |
canvas.saveState()
|
| 569 |
canvas.setFont('Helvetica', 7)
|
| 570 |
canvas.setFillColor(colors.HexColor('#666666'))
|
| 571 |
+
canvas.drawString(doc.leftMargin, 0.5*inch, f"Page {doc.page} | EvalBot HR Interview Report | Confidential")
|
| 572 |
canvas.setStrokeColor(colors.HexColor('#0050BC'))
|
| 573 |
canvas.setLineWidth(0.5)
|
| 574 |
canvas.line(doc.leftMargin, doc.height + 0.9*inch, doc.width + doc.leftMargin, doc.height + 0.9*inch)
|
|
|
|
| 678 |
line = line.strip()
|
| 679 |
if not line:
|
| 680 |
continue
|
| 681 |
+
logger.debug(f"Parsing line: {line}") # Debug parsing
|
| 682 |
if line.startswith('**') and line.endswith('**'):
|
| 683 |
section_title = line.strip('**').strip()
|
| 684 |
if section_title.startswith(('1.', '2.', '3.', '4.', '5.')):
|
|
|
|
| 698 |
elif 'Recommendations' in section_title:
|
| 699 |
current_section = 'Recommendations'
|
| 700 |
current_subsection = None
|
| 701 |
+
logger.debug(f"Set section: {current_section}")
|
| 702 |
elif line.startswith('-') and current_section:
|
| 703 |
clean_line = line.lstrip('-').strip()
|
| 704 |
if not clean_line:
|
| 705 |
continue
|
| 706 |
clean_line = re.sub(r'[^\w\s.,;:-]', '', clean_line)
|
| 707 |
+
logger.debug(f"Processing bullet: {clean_line}, section: {current_section}, subsection: {current_subsection}")
|
| 708 |
+
if current_section in ['Competency', 'Recommendations']:
|
| 709 |
+
# For dictionary sections, append to subsection
|
| 710 |
+
if current_subsection is None:
|
| 711 |
+
# Set default subsection if unset
|
| 712 |
+
if current_section == 'Competency':
|
| 713 |
+
current_subsection = 'Strengths'
|
| 714 |
+
elif current_section == 'Recommendations':
|
| 715 |
+
current_subsection = 'Development'
|
| 716 |
+
logger.debug(f"Default subsection set to: {current_subsection}")
|
|
|
|
|
|
|
| 717 |
if current_subsection:
|
| 718 |
sections[current_section][current_subsection].append(clean_line)
|
| 719 |
+
else:
|
| 720 |
+
logger.warning(f"Skipping line due to unset subsection: {clean_line}")
|
| 721 |
else:
|
| 722 |
+
# For list sections, append directly
|
| 723 |
sections[current_section].append(clean_line)
|
| 724 |
elif current_section and line:
|
| 725 |
clean_line = re.sub(r'[^\w\s.,;:-]', '', line)
|
| 726 |
+
logger.debug(f"Processing non-bullet: {clean_line}, section: {current_section}, subsection: {current_subsection}")
|
| 727 |
+
if current_section in ['Competency', 'Recommendations']:
|
| 728 |
+
if current_subsection:
|
| 729 |
+
sections[current_section][current_subsection].append(clean_line)
|
| 730 |
+
else:
|
| 731 |
+
# Default subsection
|
| 732 |
+
current_subsection = 'Strengths' if current_section == 'Competency' else 'Development'
|
| 733 |
+
sections[current_section][current_subsection].append(clean_line)
|
| 734 |
+
logger.debug(f"Default subsection for non-bullet set to: {current_subsection}")
|
| 735 |
+
else:
|
| 736 |
+
sections[current_section].append(clean_line)
|
| 737 |
|
| 738 |
# Executive Summary
|
| 739 |
story.append(Paragraph("2. Executive Summary", h2))
|
|
|
|
| 741 |
for line in sections['Executive Summary']:
|
| 742 |
story.append(Paragraph(line, bullet_style))
|
| 743 |
else:
|
| 744 |
+
story.append(Paragraph("Candidate showed moderate engagement; further assessment needed.", bullet_style))
|
| 745 |
story.append(Paragraph(f"Interview lasted {analysis_data['text_analysis']['total_duration']:.2f} seconds with {analysis_data['text_analysis']['speaker_turns']} turns.", bullet_style))
|
| 746 |
story.append(Spacer(1, 0.15*inch))
|
| 747 |
|
|
|
|
| 768 |
for line in sections['Role Fit']:
|
| 769 |
story.append(Paragraph(line, bullet_style))
|
| 770 |
else:
|
| 771 |
+
story.append(Paragraph("Potential for role fit exists; further evaluation needed to confirm alignment.", bullet_style)))
|
| 772 |
story.append(Spacer(1, 0.15*inch))
|
| 773 |
+
|
| 774 |
# Recommendations
|
| 775 |
story.append(Paragraph("5. Recommendations", h2))
|
| 776 |
story.append(Paragraph("Development Priorities", h3))
|
| 777 |
if sections['Recommendations']['Development']:
|
| 778 |
for line in sections['Recommendations']['Development']:
|
| 779 |
+
story.append(Paragraph(line, bullet_style)))
|
| 780 |
else:
|
| 781 |
story.append(Paragraph("Enroll in communication training to reduce filler words.", bullet_style))
|
| 782 |
story.append(Spacer(1, 0.1*inch))
|
|
|
|
| 793 |
logger.info(f"PDF report successfully generated at {output_path}")
|
| 794 |
return True
|
| 795 |
except Exception as e:
|
| 796 |
+
logger.error(f"PDF generation failed: {str(e)}\nFull Gemini report text:\n{gemini_report_text}", exc_info=True)
|
| 797 |
return False
|
| 798 |
|
| 799 |
def convert_to_serializable(obj):
|