AdityaSahrawat commited on
Commit
4d76db6
Β·
verified Β·
1 Parent(s): 63d7ed6

added_main.py

Browse files
Files changed (1) hide show
  1. main.py +154 -0
main.py CHANGED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Simple FastAPI Resume-JD Scorer
3
+ Accepts PDF resumes and job description, returns scored results
4
+ """
5
+
6
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from typing import List
9
+ import uvicorn
10
+ import os
11
+ import tempfile
12
+ import shutil
13
+
14
+ # Import scoring utilities
15
+ from utils.scorer import ResumeScorer
16
+ from utils.pdf_processor import extract_text_from_pdf
17
+
18
+ # Initialize FastAPI app
19
+ app = FastAPI()
20
+
21
+ # Enable CORS for frontend
22
+ app.add_middleware(
23
+ CORSMiddleware,
24
+ allow_origins=["*"],
25
+ allow_credentials=True,
26
+ allow_methods=["*"],
27
+ allow_headers=["*"],
28
+ )
29
+
30
+ # Global scorer instance
31
+ scorer = None
32
+
33
+ @app.on_event("startup")
34
+ async def startup_event():
35
+ """Load ML model on startup"""
36
+ global scorer
37
+ print("βš™οΈ Loading InstructorXL model (this may take 2-3 minutes)...")
38
+ scorer = ResumeScorer()
39
+ print("βœ… Model loaded successfully!")
40
+
41
+ @app.get("/health")
42
+ async def health():
43
+ """Health check endpoint"""
44
+ return {
45
+ "status": "healthy",
46
+ "model_loaded": scorer is not None
47
+ }
48
+
49
+ @app.post("/score")
50
+ async def score_resumes(
51
+ job_description: str = Form(...),
52
+ resumes: List[UploadFile] = File(...)
53
+ ):
54
+ """
55
+ Score resumes against job description
56
+
57
+ Parameters:
58
+ - job_description: Text of the job description
59
+ - resumes: List of PDF files
60
+
61
+ Returns:
62
+ - Ranked list of resumes with scores
63
+ """
64
+ if not scorer:
65
+ raise HTTPException(status_code=503, detail="Model not loaded yet")
66
+
67
+ if not resumes:
68
+ raise HTTPException(status_code=400, detail="No resumes provided")
69
+
70
+ if len(job_description.strip()) < 50:
71
+ raise HTTPException(status_code=400, detail="Job description too short")
72
+
73
+ results = []
74
+ temp_dir = tempfile.mkdtemp()
75
+
76
+ try:
77
+ print(f"πŸ“„ Processing {len(resumes)} resumes...")
78
+
79
+ for idx, resume_file in enumerate(resumes, 1):
80
+ print(f" [{idx}/{len(resumes)}] Processing: {resume_file.filename}")
81
+
82
+ # Validate PDF
83
+ if not resume_file.filename.lower().endswith('.pdf'):
84
+ results.append({
85
+ "resume_name": resume_file.filename,
86
+ "error": "Only PDF files supported",
87
+ "skills_score": 0.0,
88
+ "projects_score": 0.0,
89
+ "experience_score": 0.0,
90
+ "final_score": 0.0
91
+ })
92
+ continue
93
+
94
+ try:
95
+ # Save and extract PDF
96
+ temp_path = os.path.join(temp_dir, resume_file.filename)
97
+ content = await resume_file.read()
98
+
99
+ with open(temp_path, 'wb') as f:
100
+ f.write(content)
101
+
102
+ resume_text = extract_text_from_pdf(temp_path)
103
+
104
+ if not resume_text or len(resume_text.strip()) < 100:
105
+ results.append({
106
+ "resume_name": resume_file.filename,
107
+ "error": "Could not extract text from PDF",
108
+ "skills_score": 0.0,
109
+ "projects_score": 0.0,
110
+ "experience_score": 0.0,
111
+ "final_score": 0.0
112
+ })
113
+ continue
114
+
115
+ # Score resume
116
+ score_result = scorer.score_resume(job_description, resume_text)
117
+ score_result["resume_name"] = resume_file.filename
118
+ results.append(score_result)
119
+ print(f" βœ“ Scored: {resume_file.filename} (final_score: {score_result['final_score']:.3f})")
120
+
121
+ except Exception as e:
122
+ print(f"❌ Error processing {resume_file.filename}: {str(e)}")
123
+ results.append({
124
+ "resume_name": resume_file.filename,
125
+ "error": str(e),
126
+ "skills_score": 0.0,
127
+ "projects_score": 0.0,
128
+ "experience_score": 0.0,
129
+ "final_score": 0.0
130
+ })
131
+
132
+ # Sort by final score
133
+ results.sort(key=lambda x: x.get("final_score", 0), reverse=True)
134
+
135
+ print(f"βœ… Scoring complete!")
136
+
137
+ return {
138
+ "success": True,
139
+ "total": len(resumes),
140
+ "processed": len([r for r in results if "error" not in r]),
141
+ "results": results
142
+ }
143
+
144
+ finally:
145
+ # Cleanup
146
+ shutil.rmtree(temp_dir, ignore_errors=True)
147
+
148
+ if __name__ == "__main__":
149
+ uvicorn.run(
150
+ "main:app",
151
+ host="0.0.0.0",
152
+ port=8000,
153
+ reload=True
154
+ )