File size: 3,383 Bytes
1a47e5f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import pdfplumber
from sentence_transformers import SentenceTransformer, util
import gradio as gr

model = SentenceTransformer('all-MiniLM-L6-v2')

def extract_text_from_pdf(uploaded_file):
    """
    uploaded_file: Gradio file object or file-like object
    Returns extracted text
    """
    try:
        # Gradio sometimes gives a dict, sometimes a file-like object
        if hasattr(uploaded_file, "name"):  # file-like object
            path = uploaded_file.name
        elif isinstance(uploaded_file, dict) and "name" in uploaded_file:
            path = uploaded_file["name"]
        else:
            return "❌ Cannot read uploaded file."
        
        text = ""
        with pdfplumber.open(path) as pdf:
            for page in pdf.pages:
                page_text = page.extract_text()
                if page_text:
                    text += page_text + "\n"
        return text.strip() if text else "❌ No text found in PDF."
    
    except Exception as e:
        return f"❌ Error reading PDF: {e}"

common_skills = [
    "python","django","flask","fastapi","sql","postgresql","mysql","angular",
    "docker","aws","git","rest api","machine learning","pandas","numpy","appian"
]

def evaluate_fit(cv_file, cv_text, job_text):
    print(extract_text_from_pdf(cv_file))
    # Decide which CV to use
    if cv_file is not None:
        cv_text = extract_text_from_pdf(cv_file)
        if cv_text.startswith("❌"):  # extraction failed
            return cv_text
    elif cv_text:
        cv_text = cv_text
    else:
        return "❌ Please provide a CV file or paste text!"

    # --- Compute semantic similarity ---
    cv_emb = model.encode(cv_text, convert_to_tensor=True)
    job_emb = model.encode(job_text, convert_to_tensor=True)
    sim = util.cos_sim(cv_emb, job_emb).item()
    score = round(sim * 100, 2)

    # --- Skill matching ---
    job_skills = [s for s in common_skills if s in job_text.lower()]
    matched = [s for s in job_skills if s in cv_text.lower()]
    missing = [s for s in job_skills if s not in cv_text.lower()]
    skill_match = round((len(matched)/len(job_skills))*100, 2) if job_skills else 0

    # --- Build feedback ---
    if score > 70 and skill_match > 60:
        summary = f"βœ… Strong Match! (Semantic: {score}%, Skills: {skill_match}%)"
    elif score > 50:
        summary = f"⚠️ Partial Match (Semantic: {score}%, Skills: {skill_match}%)"
    else:
        summary = f"❌ Weak Match (Semantic: {score}%, Skills: {skill_match}%)"

    result = summary + "\n\n"
    if matched:
        result += f"βœ… Found skills: {', '.join(matched)}\n"
    if missing:
        result += f"❌ Missing skills: {', '.join(missing)}\n"

    # --- Extra feedback ---
    if skill_match == 100 and score < 60:
        result += "\nπŸ’‘ Tip: All required skills found, but CV wording may differ from job duties. Try describing how you used those skills."

    return result


demo = gr.Interface(
    fn=evaluate_fit,
    inputs=[
        gr.File(label="Upload CV (PDF) (optional)"),
        gr.Textbox(label="Or paste CV text (optional)", lines=6),
        gr.Textbox(label="Paste job description", lines=8)
    ],
    outputs="text",
    title="🧠 CV Fit Checker with PDF Upload",
    description="Upload your CV PDF or paste text to compare it with a job description using semantic and skill analysis."
)

demo.launch()