Chaitanya-aitf commited on
Commit
5a52dc8
·
verified ·
1 Parent(s): e5c9fc3

Update report_generator.py

Browse files
Files changed (1) hide show
  1. report_generator.py +149 -503
report_generator.py CHANGED
@@ -1,7 +1,6 @@
1
  """
2
- Report Generator Module
3
  Generates comprehensive verification reports in multiple formats
4
- Includes interview questions and export functionality
5
  """
6
 
7
  import json
@@ -10,12 +9,6 @@ import io
10
  from datetime import datetime
11
  from typing import Dict, List, Any, Optional, Tuple
12
  import pandas as pd
13
- from reportlab.lib import colors
14
- from reportlab.lib.pagesizes import letter, A4
15
- from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, PageBreak
16
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
17
- from reportlab.lib.units import inch
18
- from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
19
  import logging
20
 
21
  logger = logging.getLogger(__name__)
@@ -26,64 +19,11 @@ class ReportGenerator:
26
  """
27
 
28
  def __init__(self):
29
- """
30
- Initialize report generator
31
- """
32
- self.styles = getSampleStyleSheet()
33
- self._setup_custom_styles()
34
-
35
- def _setup_custom_styles(self):
36
- """
37
- Setup custom paragraph styles for PDF
38
- """
39
- self.styles.add(ParagraphStyle(
40
- name='CustomTitle',
41
- parent=self.styles['Heading1'],
42
- fontSize=24,
43
- textColor=colors.HexColor('#1976d2'),
44
- spaceAfter=30,
45
- alignment=TA_CENTER
46
- ))
47
-
48
- self.styles.add(ParagraphStyle(
49
- name='SectionHeader',
50
- parent=self.styles['Heading2'],
51
- fontSize=16,
52
- textColor=colors.HexColor('#424242'),
53
- spaceAfter=12,
54
- spaceBefore=12
55
- ))
56
-
57
- self.styles.add(ParagraphStyle(
58
- name='RedFlag',
59
- parent=self.styles['Normal'],
60
- fontSize=10,
61
- textColor=colors.HexColor('#d32f2f'),
62
- leftIndent=20
63
- ))
64
-
65
- self.styles.add(ParagraphStyle(
66
- name='InterviewQuestion',
67
- parent=self.styles['Normal'],
68
- fontSize=10,
69
- textColor=colors.HexColor('#1565c0'),
70
- leftIndent=20,
71
- bulletText='•'
72
- ))
73
-
74
- def generate_comprehensive_report(self,
75
- analysis_results: Dict[str, Any],
76
- output_format: str = 'pdf') -> Any:
77
- """
78
- Generate comprehensive verification report
79
-
80
- Args:
81
- analysis_results: Complete analysis results
82
- output_format: 'pdf', 'html', 'json', or 'csv'
83
-
84
- Returns:
85
- Report in specified format
86
- """
87
  if output_format == 'pdf':
88
  return self._generate_pdf_report(analysis_results)
89
  elif output_format == 'csv':
@@ -94,495 +34,201 @@ class ReportGenerator:
94
  return self._generate_html_report(analysis_results)
95
  else:
96
  raise ValueError(f"Unsupported format: {output_format}")
97
-
98
  def _generate_pdf_report(self, results: Dict[str, Any]) -> bytes:
99
- """
100
- Generate PDF report
101
- """
102
- buffer = io.BytesIO()
103
- doc = SimpleDocTemplate(buffer, pagesize=letter)
104
- story = []
105
-
106
- # Title
107
- story.append(Paragraph("Resume Verification Report", self.styles['CustomTitle']))
108
- story.append(Spacer(1, 20))
109
-
110
- # Summary Section
111
- story.append(Paragraph("Executive Summary", self.styles['SectionHeader']))
112
-
113
- summary_data = [
114
- ['Metric', 'Value'],
115
- ['Final Score', f"{results.get('final_score', 0):.1f}/100"],
116
- ['Credibility Score', f"{results.get('credibility_score', 0):.1f}/100"],
117
- ['Consistency Score', f"{results.get('consistency_score', 0):.1f}/100"],
118
- ['Risk Assessment', results.get('risk_assessment', 'Unknown')],
119
- ['Total Claims', str(results.get('total_claims', 0))],
120
- ['Verified Claims', str(results.get('verified_claims', 0))],
121
- ['Red Flags', str(results.get('total_red_flags', 0))],
122
- ['Recommendation', results.get('recommendation', 'No recommendation')]
123
- ]
124
-
125
- summary_table = Table(summary_data, colWidths=[3*inch, 3*inch])
126
- summary_table.setStyle(TableStyle([
127
- ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#e3f2fd')),
128
- ('TEXTCOLOR', (0, 0), (-1, 0), colors.HexColor('#0d47a1')),
129
- ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
130
- ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
131
- ('FONTSIZE', (0, 0), (-1, 0), 12),
132
- ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
133
- ('GRID', (0, 0), (-1, -1), 1, colors.grey),
134
- ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f5f5f5')])
135
- ]))
136
-
137
- story.append(summary_table)
138
- story.append(Spacer(1, 20))
139
-
140
- # Red Flags Section
141
- if results.get('red_flags'):
142
- story.append(Paragraph("Critical Findings", self.styles['SectionHeader']))
143
-
144
- for flag in results['red_flags'][:10]: # Top 10 red flags
145
- severity = flag.get('severity', 'unknown')
146
- color = {'critical': '#d32f2f', 'high': '#f57c00',
147
- 'medium': '#fbc02d', 'low': '#689f38'}.get(severity, '#757575')
148
-
149
- flag_style = ParagraphStyle(
150
- 'FlagStyle',
151
- parent=self.styles['Normal'],
152
- textColor=colors.HexColor(color)
153
- )
154
-
155
- story.append(Paragraph(
156
- f"[{severity.upper()}] {flag.get('description', 'No description')}",
157
- flag_style
158
- ))
159
- story.append(Spacer(1, 5))
160
-
161
- story.append(PageBreak())
162
-
163
- # Interview Questions Section
164
- story.append(Paragraph("Recommended Interview Questions", self.styles['SectionHeader']))
165
-
166
- questions = self._compile_interview_questions(results)
167
-
168
- for i, question in enumerate(questions[:15], 1): # Top 15 questions
169
- story.append(Paragraph(
170
- f"{i}. {question}",
171
- self.styles['InterviewQuestion']
172
- ))
173
- story.append(Spacer(1, 8))
174
-
175
- # Detailed Claims Analysis
176
- story.append(PageBreak())
177
- story.append(Paragraph("Claims Analysis Detail", self.styles['SectionHeader']))
178
-
179
- if results.get('claims'):
180
- claims_data = [['Claim', 'Category', 'Evidence', 'Status']]
181
-
182
- for claim in results['claims'][:20]: # Top 20 claims
183
- claims_data.append([
184
- claim.get('claim_text', '')[:50] + '...',
185
- claim.get('category', 'unknown'),
186
- claim.get('evidence_present', 'none'),
187
- claim.get('verification_status', 'unknown')
188
- ])
189
-
190
- claims_table = Table(claims_data, colWidths=[2.5*inch, 1.5*inch, 1.5*inch, 1.5*inch])
191
- claims_table.setStyle(TableStyle([
192
- ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#e8f5e9')),
193
- ('TEXTCOLOR', (0, 0), (-1, 0), colors.HexColor('#1b5e20')),
194
- ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
195
- ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
196
- ('FONTSIZE', (0, 0), (-1, -1), 9),
197
- ('GRID', (0, 0), (-1, -1), 1, colors.grey),
198
- ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f5f5f5')])
199
- ]))
200
-
201
- story.append(claims_table)
202
-
203
- # Build PDF
204
- doc.build(story)
205
- buffer.seek(0)
206
- return buffer.getvalue()
207
-
208
  def _generate_csv_report(self, results: Dict[str, Any]) -> str:
209
- """
210
- Generate CSV report
211
- """
212
  output = io.StringIO()
213
 
214
- # Claims CSV
215
  if results.get('claims'):
216
- claims_df = pd.DataFrame(results['claims'])
217
-
218
- # Select relevant columns
219
- columns = ['claim_id', 'claim_text', 'category', 'subcategory',
220
- 'verifiability_level', 'evidence_present', 'verification_status',
221
- 'final_evidence_score', 'confidence_score']
222
-
223
- # Filter existing columns
224
- existing_cols = [col for col in columns if col in claims_df.columns]
225
- claims_df = claims_df[existing_cols]
226
-
227
- claims_df.to_csv(output, index=False)
228
-
229
- csv_content = output.getvalue()
230
- output.close()
231
-
232
- return csv_content
233
-
234
  def _generate_json_report(self, results: Dict[str, Any]) -> str:
235
- """
236
- Generate JSON report
237
- """
238
- # Clean up for JSON serialization
239
  clean_results = self._clean_for_json(results)
240
-
241
  return json.dumps(clean_results, indent=2, default=str)
242
-
243
  def _generate_html_report(self, results: Dict[str, Any]) -> str:
244
- """
245
- Generate HTML report
246
- """
247
- html_template = """
248
  <!DOCTYPE html>
249
  <html>
250
  <head>
251
  <title>Resume Verification Report</title>
252
- <meta charset="utf-8">
253
  <style>
254
- body {{
255
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
256
- line-height: 1.6;
257
- color: #333;
258
- max-width: 1200px;
259
- margin: 0 auto;
260
- padding: 20px;
261
- background: #f5f5f5;
262
- }}
263
-
264
- .header {{
265
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
266
- color: white;
267
- padding: 30px;
268
- border-radius: 10px;
269
- margin-bottom: 30px;
270
- }}
271
-
272
- .score-card {{
273
- display: grid;
274
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
275
- gap: 20px;
276
- margin: 30px 0;
277
- }}
278
-
279
- .card {{
280
- background: white;
281
- padding: 20px;
282
- border-radius: 8px;
283
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
284
- }}
285
-
286
- .card h3 {{
287
- margin-top: 0;
288
- color: #667eea;
289
- }}
290
-
291
- .score {{
292
- font-size: 36px;
293
- font-weight: bold;
294
- color: #333;
295
- }}
296
-
297
- .red-flag {{
298
- background: #ffebee;
299
- border-left: 4px solid #d32f2f;
300
- padding: 10px;
301
- margin: 10px 0;
302
- }}
303
-
304
- .warning {{
305
- background: #fff3e0;
306
- border-left: 4px solid #f57c00;
307
- padding: 10px;
308
- margin: 10px 0;
309
- }}
310
-
311
- .success {{
312
- background: #e8f5e9;
313
- border-left: 4px solid #4caf50;
314
- padding: 10px;
315
- margin: 10px 0;
316
- }}
317
-
318
- table {{
319
- width: 100%;
320
- border-collapse: collapse;
321
- margin: 20px 0;
322
- background: white;
323
- }}
324
-
325
- th {{
326
- background: #667eea;
327
- color: white;
328
- padding: 12px;
329
- text-align: left;
330
- }}
331
-
332
- td {{
333
- padding: 10px;
334
- border-bottom: 1px solid #ddd;
335
- }}
336
-
337
- tr:hover {{
338
- background: #f5f5f5;
339
- }}
340
-
341
- .interview-questions {{
342
- background: white;
343
- padding: 20px;
344
- border-radius: 8px;
345
- margin: 20px 0;
346
- }}
347
-
348
- .question {{
349
- padding: 10px;
350
- margin: 10px 0;
351
- background: #e3f2fd;
352
- border-left: 3px solid #2196f3;
353
- }}
354
  </style>
355
  </head>
356
  <body>
357
  <div class="header">
358
  <h1>Resume Verification Report</h1>
359
- <p>Generated on {timestamp}</p>
360
- </div>
361
-
362
- <div class="score-card">
363
- <div class="card">
364
- <h3>Final Score</h3>
365
- <div class="score">{final_score:.0f}</div>
366
- <p>Overall assessment score</p>
367
- </div>
368
-
369
- <div class="card">
370
- <h3>Credibility</h3>
371
- <div class="score">{credibility_score:.0f}</div>
372
- <p>Evidence-based credibility</p>
373
- </div>
374
-
375
- <div class="card">
376
- <h3>Consistency</h3>
377
- <div class="score">{consistency_score:.0f}</div>
378
- <p>Cross-section consistency</p>
379
- </div>
380
-
381
- <div class="card">
382
- <h3>Risk Level</h3>
383
- <div class="score" style="font-size: 24px;">{risk_level}</div>
384
- <p>Overall risk assessment</p>
385
- </div>
386
  </div>
387
 
388
  <div class="card">
389
- <h2>Summary Statistics</h2>
390
- <table>
391
- <tr>
392
- <th>Metric</th>
393
- <th>Value</th>
394
- </tr>
395
- <tr>
396
- <td>Total Claims Analyzed</td>
397
- <td>{total_claims}</td>
398
- </tr>
399
- <tr>
400
- <td>Verified Claims</td>
401
- <td>{verified_claims}</td>
402
- </tr>
403
- <tr>
404
- <td>Unverified Claims</td>
405
- <td>{unverified_claims}</td>
406
- </tr>
407
- <tr>
408
- <td>Red Flags Detected</td>
409
- <td>{total_red_flags}</td>
410
- </tr>
411
- </table>
412
  </div>
413
 
414
  <div class="card">
415
- <h2>Critical Findings</h2>
416
- {red_flags_html}
417
- </div>
418
-
419
- <div class="interview-questions">
420
- <h2>Recommended Interview Questions</h2>
421
- {questions_html}
422
  </div>
423
 
424
  <div class="card">
425
  <h2>Recommendation</h2>
426
- <p>{recommendation}</p>
427
  </div>
428
  </body>
429
  </html>
430
  """
431
-
432
- # Generate red flags HTML
433
- red_flags_html = ""
434
- if results.get('red_flags'):
435
- for flag in results['red_flags'][:10]:
436
- severity = flag.get('severity', 'unknown')
437
- css_class = 'red-flag' if severity in ['critical', 'high'] else 'warning'
438
- red_flags_html += f'<div class="{css_class}">'
439
- red_flags_html += f'<strong>[{severity.upper()}]</strong> {flag.get("description", "")}'
440
- red_flags_html += '</div>'
441
- else:
442
- red_flags_html = '<p class="success">No critical red flags detected</p>'
443
-
444
- # Generate questions HTML
445
- questions = self._compile_interview_questions(results)
446
- questions_html = ""
447
- for i, q in enumerate(questions[:10], 1):
448
- questions_html += f'<div class="question">{i}. {q}</div>'
449
-
450
- # Fill template
451
- html = html_template.format(
452
- timestamp=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
453
- final_score=results.get('final_score', 0),
454
- credibility_score=results.get('credibility_score', 0),
455
- consistency_score=results.get('consistency_score', 0),
456
- risk_level=results.get('risk_assessment', 'Unknown').upper(),
457
- total_claims=results.get('total_claims', 0),
458
- verified_claims=results.get('verified_claims', 0),
459
- unverified_claims=results.get('unverified_claims', 0),
460
- total_red_flags=results.get('total_red_flags', 0),
461
- red_flags_html=red_flags_html,
462
- questions_html=questions_html,
463
- recommendation=results.get('recommendation', 'No recommendation available')
464
- )
465
-
466
  return html
 
 
 
 
 
 
 
 
 
 
 
 
467
 
468
- def _compile_interview_questions(self, results: Dict[str, Any]) -> List[str]:
469
- """
470
- Compile all interview questions from various sources
471
- """
472
- questions = []
473
- seen = set()
474
-
475
- # Questions from red flags
476
- if results.get('red_flags'):
477
- for flag in results['red_flags']:
478
- if flag.get('interview_questions'):
479
- for q in flag['interview_questions']:
480
- if q not in seen:
481
- questions.append(q)
482
- seen.add(q)
483
-
484
- # Questions from SOTA validations
485
- if results.get('sota_validations'):
486
- for val in results['sota_validations']:
487
- if val.get('interview_questions'):
488
- for q in val['interview_questions']:
489
- if q not in seen:
490
- questions.append(q)
491
- seen.add(q)
492
-
493
- # Questions from evidence validations
494
- if results.get('validations'):
495
- for val in results['validations']:
496
- if val.get('needs_clarification'):
497
- for item in val['needs_clarification']:
498
- q = f"Can you provide more details about: {item}?"
499
- if q not in seen:
500
- questions.append(q)
501
- seen.add(q)
502
-
503
- # Add general questions if few specific ones
504
- if len(questions) < 5:
505
- general_questions = [
506
- "Walk me through your most challenging project and how you overcame obstacles.",
507
- "Can you provide references who can verify your key achievements?",
508
- "What specific metrics did you use to measure success in your previous role?",
509
- "Describe a failure or setback and what you learned from it.",
510
- "How do you stay current with technology trends in your field?"
511
- ]
512
-
513
- for q in general_questions:
514
- if q not in seen:
515
- questions.append(q)
516
- seen.add(q)
517
-
518
- return questions
519
-
520
  def _clean_for_json(self, obj: Any) -> Any:
521
- """
522
- Clean object for JSON serialization
523
- """
524
  if isinstance(obj, dict):
525
  return {k: self._clean_for_json(v) for k, v in obj.items()}
526
  elif isinstance(obj, list):
527
  return [self._clean_for_json(item) for item in obj]
528
- elif isinstance(obj, (datetime,)):
529
  return obj.isoformat()
530
  elif hasattr(obj, '__dict__'):
531
  return self._clean_for_json(obj.__dict__)
532
  else:
533
  return obj
534
-
535
- def generate_interview_checklist(self, results: Dict[str, Any]) -> str:
536
- """
537
- Generate a printable interview checklist
538
- """
539
- checklist = "INTERVIEW VERIFICATION CHECKLIST\n"
540
- checklist += "=" * 50 + "\n\n"
541
 
542
- checklist += f"Candidate: ________________________\n"
543
- checklist += f"Date: {datetime.now().strftime('%Y-%m-%d')}\n"
544
- checklist += f"Interviewer: ________________________\n\n"
 
 
 
 
 
545
 
546
- checklist += "RISK ASSESSMENT\n"
547
- checklist += "-" * 30 + "\n"
548
- checklist += f"Overall Risk Level: {results.get('risk_assessment', 'Unknown').upper()}\n"
549
- checklist += f"Final Score: {results.get('final_score', 0):.0f}/100\n"
550
- checklist += f"Red Flags: {results.get('total_red_flags', 0)}\n\n"
 
 
 
 
 
 
 
 
 
551
 
552
- checklist += "KEY AREAS TO PROBE\n"
553
- checklist += "-" * 30 + "\n"
 
 
 
 
554
 
555
- # Top red flags
556
- if results.get('red_flags'):
557
- for i, flag in enumerate(results['red_flags'][:5], 1):
558
- checklist += f"{i}. [{flag.get('severity', '').upper()}] {flag.get('description', '')}\n"
559
- checklist += f" □ Verified □ Explained □ Unsatisfactory\n\n"
560
-
561
- checklist += "\nREQUIRED QUESTIONS\n"
562
- checklist += "-" * 30 + "\n"
563
 
 
564
  questions = self._compile_interview_questions(results)
 
565
  for i, q in enumerate(questions[:10], 1):
566
  checklist += f"{i}. {q}\n"
567
- checklist += " Response: _________________________________________\n"
568
- checklist += " □ Satisfactory □ Needs Follow-up □ Red Flag\n\n"
569
-
570
- checklist += "\nDOCUMENTS TO REQUEST\n"
571
- checklist += "-" * 30 + "\n"
572
- checklist += " Portfolio/GitHub links\n"
573
- checklist += " Reference contacts\n"
574
- checklist += "□ Certificates/credentials\n"
575
- checklist += " Performance reviews\n"
576
- checklist += " Work samples\n\n"
577
-
578
- checklist += "FINAL ASSESSMENT\n"
579
- checklist += "-" * 30 + "\n"
580
- checklist += "□ Strongly Recommend\n"
581
- checklist += "□ Recommend with Reservations\n"
582
- checklist += "□ Neutral\n"
583
- checklist += "□ Do Not Recommend\n\n"
584
-
585
- checklist += "Notes:\n"
586
- checklist += "_" * 50 + "\n" * 5
587
 
588
  return checklist
 
1
  """
2
+ Report Generator Module - Simplified for HF Spaces
3
  Generates comprehensive verification reports in multiple formats
 
4
  """
5
 
6
  import json
 
9
  from datetime import datetime
10
  from typing import Dict, List, Any, Optional, Tuple
11
  import pandas as pd
 
 
 
 
 
 
12
  import logging
13
 
14
  logger = logging.getLogger(__name__)
 
19
  """
20
 
21
  def __init__(self):
22
+ """Initialize report generator"""
23
+ self.styles = {}
24
+
25
+ def generate_comprehensive_report(self, analysis_results: Dict[str, Any], output_format: str = 'pdf') -> Any:
26
+ """Generate report in specified format"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  if output_format == 'pdf':
28
  return self._generate_pdf_report(analysis_results)
29
  elif output_format == 'csv':
 
34
  return self._generate_html_report(analysis_results)
35
  else:
36
  raise ValueError(f"Unsupported format: {output_format}")
37
+
38
  def _generate_pdf_report(self, results: Dict[str, Any]) -> bytes:
39
+ """Generate simple PDF report"""
40
+ # Simplified PDF generation for HF Spaces
41
+ try:
42
+ from reportlab.lib import colors
43
+ from reportlab.lib.pagesizes import letter
44
+ from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
45
+ from reportlab.lib.styles import getSampleStyleSheet
46
+ from reportlab.lib.units import inch
47
+
48
+ buffer = io.BytesIO()
49
+ doc = SimpleDocTemplate(buffer, pagesize=letter)
50
+ story = []
51
+ styles = getSampleStyleSheet()
52
+
53
+ # Title
54
+ story.append(Paragraph("Resume Verification Report", styles['Title']))
55
+ story.append(Spacer(1, 20))
56
+
57
+ # Summary
58
+ summary_text = f"""
59
+ Final Score: {results.get('final_score', 0):.1f}/100<br/>
60
+ Risk Level: {results.get('risk_assessment', 'Unknown')}<br/>
61
+ Total Claims: {results.get('total_claims', 0)}<br/>
62
+ Verified: {results.get('verified_claims', 0)}<br/>
63
+ Red Flags: {results.get('total_red_flags', 0)}<br/>
64
+ """
65
+ story.append(Paragraph(summary_text, styles['Normal']))
66
+
67
+ # Build PDF
68
+ doc.build(story)
69
+ buffer.seek(0)
70
+ return buffer.getvalue()
71
+
72
+ except ImportError:
73
+ # Fallback if reportlab not available
74
+ return b"PDF generation not available. Please use HTML or JSON format."
75
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  def _generate_csv_report(self, results: Dict[str, Any]) -> str:
77
+ """Generate CSV report"""
 
 
78
  output = io.StringIO()
79
 
 
80
  if results.get('claims'):
81
+ # Create DataFrame from claims
82
+ claims_data = []
83
+ for claim in results['claims'][:100]: # Limit to 100
84
+ claims_data.append({
85
+ 'claim_id': claim.get('claim_id', ''),
86
+ 'claim_text': claim.get('claim_text', ''),
87
+ 'category': claim.get('category', ''),
88
+ 'verifiability_level': claim.get('verifiability_level', ''),
89
+ 'evidence_present': claim.get('evidence_present', '')
90
+ })
91
+
92
+ df = pd.DataFrame(claims_data)
93
+ df.to_csv(output, index=False)
94
+
95
+ return output.getvalue()
96
+
 
 
97
  def _generate_json_report(self, results: Dict[str, Any]) -> str:
98
+ """Generate JSON report"""
99
+ # Clean results for JSON serialization
 
 
100
  clean_results = self._clean_for_json(results)
 
101
  return json.dumps(clean_results, indent=2, default=str)
102
+
103
  def _generate_html_report(self, results: Dict[str, Any]) -> str:
104
+ """Generate HTML report"""
105
+ html = f"""
 
 
106
  <!DOCTYPE html>
107
  <html>
108
  <head>
109
  <title>Resume Verification Report</title>
 
110
  <style>
111
+ body {{ font-family: Arial, sans-serif; margin: 20px; }}
112
+ .header {{ background: #4a90e2; color: white; padding: 20px; }}
113
+ .score {{ font-size: 36px; font-weight: bold; }}
114
+ .card {{ background: #f5f5f5; padding: 15px; margin: 10px 0; }}
115
+ .red-flag {{ background: #ffebee; border-left: 4px solid #f44336; padding: 10px; margin: 5px 0; }}
116
+ table {{ width: 100%; border-collapse: collapse; }}
117
+ th, td {{ padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  </style>
119
  </head>
120
  <body>
121
  <div class="header">
122
  <h1>Resume Verification Report</h1>
123
+ <p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  </div>
125
 
126
  <div class="card">
127
+ <h2>Summary</h2>
128
+ <div class="score">{results.get('final_score', 0):.0f}/100</div>
129
+ <p>Risk Level: {results.get('risk_assessment', 'Unknown').upper()}</p>
130
+ <p>Total Claims: {results.get('total_claims', 0)}</p>
131
+ <p>Verified: {results.get('verified_claims', 0)}</p>
132
+ <p>Red Flags: {results.get('total_red_flags', 0)}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  </div>
134
 
135
  <div class="card">
136
+ <h2>Red Flags</h2>
137
+ {self._generate_red_flags_html(results.get('red_flags', []))}
 
 
 
 
 
138
  </div>
139
 
140
  <div class="card">
141
  <h2>Recommendation</h2>
142
+ <p>{results.get('recommendation', 'No recommendation available')}</p>
143
  </div>
144
  </body>
145
  </html>
146
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  return html
148
+
149
+ def _generate_red_flags_html(self, red_flags: List[Dict]) -> str:
150
+ """Generate HTML for red flags"""
151
+ if not red_flags:
152
+ return "<p>No red flags detected</p>"
153
+
154
+ html = ""
155
+ for flag in red_flags[:10]: # Limit to 10
156
+ html += f'<div class="red-flag">'
157
+ html += f'<strong>[{flag.get("severity", "").upper()}]</strong> '
158
+ html += f'{flag.get("description", "No description")}'
159
+ html += '</div>'
160
 
161
+ return html
162
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  def _clean_for_json(self, obj: Any) -> Any:
164
+ """Clean object for JSON serialization"""
 
 
165
  if isinstance(obj, dict):
166
  return {k: self._clean_for_json(v) for k, v in obj.items()}
167
  elif isinstance(obj, list):
168
  return [self._clean_for_json(item) for item in obj]
169
+ elif isinstance(obj, datetime):
170
  return obj.isoformat()
171
  elif hasattr(obj, '__dict__'):
172
  return self._clean_for_json(obj.__dict__)
173
  else:
174
  return obj
175
+
176
+ def _compile_interview_questions(self, results: Dict[str, Any]) -> List[str]:
177
+ """Compile interview questions from analysis"""
178
+ questions = []
179
+ seen = set()
 
 
180
 
181
+ # From red flags
182
+ if results.get('red_flags'):
183
+ for flag in results['red_flags'][:10]:
184
+ if flag.get('interview_probe'):
185
+ q = flag['interview_probe']
186
+ if q not in seen:
187
+ questions.append(q)
188
+ seen.add(q)
189
 
190
+ # Add general questions if needed
191
+ if len(questions) < 5:
192
+ general = [
193
+ "Walk me through your most challenging project.",
194
+ "Can you provide references for your achievements?",
195
+ "What metrics did you use to measure success?",
196
+ "How do you stay current with technology?",
197
+ "Describe a failure and what you learned."
198
+ ]
199
+
200
+ for q in general:
201
+ if q not in seen and len(questions) < 10:
202
+ questions.append(q)
203
+ seen.add(q)
204
 
205
+ return questions
206
+
207
+ def generate_interview_checklist(self, results: Dict[str, Any]) -> str:
208
+ """Generate interview checklist"""
209
+ checklist = "INTERVIEW CHECKLIST\n"
210
+ checklist += "=" * 40 + "\n\n"
211
 
212
+ checklist += f"Date: {datetime.now().strftime('%Y-%m-%d')}\n"
213
+ checklist += f"Risk Level: {results.get('risk_assessment', 'Unknown')}\n"
214
+ checklist += f"Final Score: {results.get('final_score', 0):.0f}/100\n\n"
 
 
 
 
 
215
 
216
+ checklist += "KEY QUESTIONS:\n"
217
  questions = self._compile_interview_questions(results)
218
+
219
  for i, q in enumerate(questions[:10], 1):
220
  checklist += f"{i}. {q}\n"
221
+ checklist += " [ ] Answered\n\n"
222
+
223
+ checklist += "\nDOCUMENTS TO REQUEST:\n"
224
+ checklist += "[ ] Portfolio/GitHub\n"
225
+ checklist += "[ ] References\n"
226
+ checklist += "[ ] Certificates\n"
227
+ checklist += "[ ] Work samples\n\n"
228
+
229
+ checklist += "FINAL ASSESSMENT:\n"
230
+ checklist += "[ ] Recommend\n"
231
+ checklist += "[ ] Recommend with reservations\n"
232
+ checklist += "[ ] Do not recommend\n"
 
 
 
 
 
 
 
 
233
 
234
  return checklist