Dev1012 commited on
Commit
b4b6f00
·
1 Parent(s): 6c4cb5c

adding ats suggestion code(basic)

Browse files
Files changed (3) hide show
  1. ai_suggestions.py +0 -0
  2. app.py +21 -8
  3. ats_core.py +0 -28
ai_suggestions.py ADDED
File without changes
app.py CHANGED
@@ -2,20 +2,28 @@ from fastapi import FastAPI, UploadFile, File, Form
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from ats_core import extract_pdf_text, ats_score
4
  from role_templates import ROLE_TEMPLATES
5
-
6
  app = FastAPI()
7
 
8
- app.add_middleware(
9
- CORSMiddleware,
10
- allow_origins=["*"],
11
- allow_methods=["*"],
12
- allow_headers=["*"],
13
- )
14
 
15
  @app.get("/health")
16
  def health():
17
  return {"status": "ok"}
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  @app.post("/ats-score")
20
  async def score(
21
  resume: UploadFile = File(...),
@@ -31,4 +39,9 @@ async def score(
31
  if not jd:
32
  return {"error": "Invalid role"}
33
 
34
- return ats_score(resume_text, jd, role)
 
 
 
 
 
 
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from ats_core import extract_pdf_text, ats_score
4
  from role_templates import ROLE_TEMPLATES
5
+ from ai_suggestions import generate_suggestions
6
  app = FastAPI()
7
 
8
+ app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"],)
 
 
 
 
 
9
 
10
  @app.get("/health")
11
  def health():
12
  return {"status": "ok"}
13
 
14
+ # @app.post("/ats-score")
15
+ # async def score( resume: UploadFile = File(...), role: str = Form(...), job_description: str = Form("") ):
16
+ # pdf_bytes = await resume.read()
17
+ # resume_text = extract_pdf_text(pdf_bytes)
18
+
19
+ # role = role.lower().strip()
20
+ # jd = job_description if job_description.strip() else ROLE_TEMPLATES.get(role, "")
21
+
22
+ # if not jd:
23
+ # return {"error": "Invalid role"}
24
+
25
+ # return ats_score(resume_text, jd, role)
26
+
27
  @app.post("/ats-score")
28
  async def score(
29
  resume: UploadFile = File(...),
 
39
  if not jd:
40
  return {"error": "Invalid role"}
41
 
42
+ ats_result = ats_score(resume_text, jd, role)
43
+
44
+ ai_text = generate_suggestions(resume_text, jd, ats_result)
45
+
46
+ ats_result["ai_suggestions"] = ai_text
47
+ return ats_result
ats_core.py CHANGED
@@ -12,25 +12,12 @@ from nltk.corpus import stopwords
12
  from nltk import pos_tag
13
  from nltk.tokenize import wordpunct_tokenize # ✅ SAFE TOKENIZER
14
 
15
- # ----------------------------
16
- # Logging
17
- # ----------------------------
18
  logging.getLogger("pdfminer").setLevel(logging.ERROR)
19
 
20
- # ----------------------------
21
- # REQUIRED NLTK ASSETS
22
- # (download ONLY in Docker build)
23
- # ----------------------------
24
  STOPWORDS = set(stopwords.words("english"))
25
 
26
- # ----------------------------
27
- # Embedding model
28
- # ----------------------------
29
  model = SentenceTransformer("all-MiniLM-L6-v2")
30
 
31
- # ----------------------------
32
- # Helpers
33
- # ----------------------------
34
  def f(x):
35
  return float(round(x, 2))
36
 
@@ -39,9 +26,6 @@ def clean(text: str) -> str:
39
  text = re.sub(r"[^a-z0-9\s\-]", " ", text)
40
  return re.sub(r"\s+", " ", text).strip()
41
 
42
- # ----------------------------
43
- # Resume text extraction
44
- # ----------------------------
45
  def extract_pdf_text(file_bytes: bytes) -> str:
46
  text = ""
47
  with pdfplumber.open(BytesIO(file_bytes)) as pdf:
@@ -50,9 +34,6 @@ def extract_pdf_text(file_bytes: bytes) -> str:
50
  text += page.extract_text() + "\n"
51
  return text.strip()
52
 
53
- # ----------------------------
54
- # Contact info extraction
55
- # ----------------------------
56
  def extract_email(text: str):
57
  m = re.search(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", text)
58
  return m.group(0) if m else None
@@ -84,9 +65,6 @@ def extract_name(text: str):
84
  return line
85
  return None
86
 
87
- # ----------------------------
88
- # NLP scoring
89
- # ----------------------------
90
  def embed_sim(a: str, b: str) -> float:
91
  emb = model.encode([a, b])
92
  return float(cosine_similarity([emb[0]], [emb[1]])[0][0])
@@ -131,9 +109,6 @@ def generic_penalty(text: str) -> float:
131
  penalty = sum(0.05 for g in GENERIC if g in text)
132
  return min(penalty, 0.20)
133
 
134
- # ----------------------------
135
- # Verdict + calibration
136
- # ----------------------------
137
  def verdict(score: float) -> str:
138
  if score < 35: return "Poor Fit"
139
  if score < 50: return "Below Average"
@@ -147,9 +122,6 @@ def calibrate(raw: float) -> float:
147
  scaled = (raw - RAW_MIN) / (RAW_MAX - RAW_MIN)
148
  return 20 + scaled * 70 # → [20, 90]
149
 
150
- # ----------------------------
151
- # MAIN ENTRY
152
- # ----------------------------
153
  def ats_score(resume_text: str, jd_text: str, role: str) -> Dict[str, Any]:
154
  resume_clean = clean(resume_text)
155
  jd_clean = clean(jd_text)
 
12
  from nltk import pos_tag
13
  from nltk.tokenize import wordpunct_tokenize # ✅ SAFE TOKENIZER
14
 
 
 
 
15
  logging.getLogger("pdfminer").setLevel(logging.ERROR)
16
 
 
 
 
 
17
  STOPWORDS = set(stopwords.words("english"))
18
 
 
 
 
19
  model = SentenceTransformer("all-MiniLM-L6-v2")
20
 
 
 
 
21
  def f(x):
22
  return float(round(x, 2))
23
 
 
26
  text = re.sub(r"[^a-z0-9\s\-]", " ", text)
27
  return re.sub(r"\s+", " ", text).strip()
28
 
 
 
 
29
  def extract_pdf_text(file_bytes: bytes) -> str:
30
  text = ""
31
  with pdfplumber.open(BytesIO(file_bytes)) as pdf:
 
34
  text += page.extract_text() + "\n"
35
  return text.strip()
36
 
 
 
 
37
  def extract_email(text: str):
38
  m = re.search(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", text)
39
  return m.group(0) if m else None
 
65
  return line
66
  return None
67
 
 
 
 
68
  def embed_sim(a: str, b: str) -> float:
69
  emb = model.encode([a, b])
70
  return float(cosine_similarity([emb[0]], [emb[1]])[0][0])
 
109
  penalty = sum(0.05 for g in GENERIC if g in text)
110
  return min(penalty, 0.20)
111
 
 
 
 
112
  def verdict(score: float) -> str:
113
  if score < 35: return "Poor Fit"
114
  if score < 50: return "Below Average"
 
122
  scaled = (raw - RAW_MIN) / (RAW_MAX - RAW_MIN)
123
  return 20 + scaled * 70 # → [20, 90]
124
 
 
 
 
125
  def ats_score(resume_text: str, jd_text: str, role: str) -> Dict[str, Any]:
126
  resume_clean = clean(resume_text)
127
  jd_clean = clean(jd_text)