File size: 3,566 Bytes
af02e64
 
 
 
 
 
775c09c
af02e64
775c09c
50d928c
af02e64
 
775c09c
af02e64
 
 
 
 
 
 
 
 
 
 
 
 
775c09c
af02e64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775c09c
af02e64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775c09c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af02e64
 
775c09c
af02e64
 
775c09c
af02e64
 
775c09c
 
 
 
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
from __future__ import annotations
import os
import re
import subprocess
import zipfile
from typing import List
from transformers import pipeline

# Load the NER model for resume parsing
ner = pipeline("ner", model="Kiet/ResumeParserBERT", aggregation_strategy="simple")

def extract_text(file_path: str) -> str:
    """Extract text from PDF or DOCX."""
    if not file_path or not os.path.isfile(file_path):
        return ""

    lower_name = file_path.lower()
    try:
        if lower_name.endswith('.pdf'):
            try:
                result = subprocess.run(
                    ['pdftotext', '-layout', file_path, '-'],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    check=False
                )
                return result.stdout.decode('utf-8', errors='ignore')
            except Exception:
                return ""
        elif lower_name.endswith('.docx'):
            try:
                with zipfile.ZipFile(file_path) as zf:
                    with zf.open('word/document.xml') as docx_xml:
                        xml_bytes = docx_xml.read()
                        xml_text = xml_bytes.decode('utf-8', errors='ignore')
                        xml_text = re.sub(r'<w:p[^>]*>', '\n', xml_text, flags=re.I)
                        text = re.sub(r'<[^>]+>', ' ', xml_text)
                        text = re.sub(r'\s+', ' ', text)
                        return text
            except Exception:
                return ""
        else:
            return ""
    except Exception:
        return ""

def extract_name(text: str, filename: str) -> str:
    """Extract candidate's name from text or filename."""
    if text:
        lines = [ln.strip() for ln in text.splitlines() if ln.strip()]
        for line in lines[:10]:
            if re.match(r'(?i)resume|curriculum vitae', line):
                continue
            words = line.split()
            if 1 < len(words) <= 4:
                if all(re.match(r'^[A-ZÀ-ÖØ-Þ][\w\-]*', w) for w in words):
                    return line
    base = os.path.basename(filename)
    base = re.sub(r'\.(pdf|docx|doc)$', '', base, flags=re.I)
    base = re.sub(r'[\._-]+', ' ', base)
    base = re.sub(r'(?i)\b(cv|resume)\b', '', base)
    base = re.sub(r'\s+', ' ', base).strip()
    return base.title() if base else ''

def extract_entities(text: str) -> dict:
    """Extract structured info using NER model."""
    entities = ner(text)
    skills, education, experience = [], [], []
    for ent in entities:
        label = ent['entity_group'].upper()
        word = ent['word'].strip()
        if label in ["SKILL", "TECH", "TECHNOLOGY"]:
            skills.append(word)
        elif label in ["EDUCATION", "DEGREE", "QUALIFICATION"]:
            education.append(word)
        elif label in ["EXPERIENCE", "JOB", "ROLE"]:
            experience.append(word)
    return {
        "skills": list(dict.fromkeys(skills)),
        "education": list(dict.fromkeys(education)),
        "experience": list(dict.fromkeys(experience))
    }

def parse_resume(file_path: str, filename: str) -> dict:
    """Main function to parse resume fields."""
    text = extract_text(file_path)
    name = extract_name(text, filename)
    ents = extract_entities(text)
    return {
        'name': name or '',
        'skills': ', '.join(ents["skills"]) if ents["skills"] else '',
        'education': ', '.join(ents["education"]) if ents["education"] else '',
        'experience': ', '.join(ents["experience"]) if ents["experience"] else ''
    }