File size: 3,901 Bytes
b8b3ced
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import spacy
from rapidfuzz import process
from nltk import ngrams
from nltk.tokenize import word_tokenize
from huggingface_hub import snapshot_download


class SkillListMatcher:
    """
    Provides methods to extract and match skills from text.
    """
    def __init__(self, spacy_model= "en_core_web_sm"):
        """
        Initializes the matcher and loads the spaCy model.
        :param spacy_model: Name of the spaCy model to load.
        """
        if not spacy.util.is_package(spacy_model):
            spacy.cli.download(spacy_model)

        self.nlp = spacy.load(spacy_model)


    def __lemmatization(self, skills):
        """
        Lemmatizes a list of skills.
        :param skills: List of skill strings.
        :return: List of lemmatized skills.
        """
        new_skills = []
        for i in range(len(skills)):
            skill = skills[i]
            doc = self.nlp(skill)
            tokens = [token.lemma_ for token in doc]
            new_skills.append(" ".join(tokens).lower().strip())

        return new_skills


    def extract(self, text, skills, threshold=95):
        """
        Extracts relevant skills from the given text.
        :param text: The input text.
        :param skills: List of reference skill strings.
        :param threshold: Threshold for matching skills.
        :return: List of matched skills found in the text.
        """
        text = text.lower()
        tokens = word_tokenize(text)

        candidates = set()
        for n in range(1, 5):
            for gram in ngrams(tokens, n):
                phrase = ' '.join(gram)
                candidates.add(phrase)

        new_skills = self.__lemmatization(skills)

        found_skills = set()
        for phrase in candidates:
            match, score, _ = process.extractOne(phrase, new_skills)
            if score >= threshold:
                found_skills.add(match)

        return list(found_skills)


    def match(self, main_skills, extract_skills):
        """
        Matches extracted skills with main skills.
        :param main_skills: List of target skill strings.
        :param extract_skills: List of extracted skill strings.
        :return: Tuple of match ratio and formatted match string.
        """
        main_skills = self.__lemmatization(main_skills)
        extract_skills = self.__lemmatization(extract_skills)

        count = 0

        for skill in extract_skills:
            if skill in main_skills:
                count += 1

        return count / len(main_skills), f"{count}/{len(main_skills)}"


class SkillDynamicMatcher:
    """
    Extracts and matches skills using a trained spaCy NER model.
    """
    def __init__(self, model_path="amjad-awad/skill-extractor"):
        """
        Initializes the NER model from the specified path.
        :param model_path: Path to the trained NER model.
        """
        model_path = snapshot_download(model_path, repo_type="model")
        self.ner_model = spacy.load(model_path)


    def extract(self, text):
        """
        Extracts skill entities from the input text.
        :param text: The input text.
        :return: List of extracted skill entities.
        """
        skills = []
        doc = self.ner_model(text)

        for ent in doc.ents:
            if "SKILLS" in ent.label_:
                skills.append(ent.text.lower())

        return list(set(skills))


    def match(self, main_skills, extract_skills):
        """
        Matches extracted skills with main skills.
        :param main_skills: List of target skill strings.
        :param extract_skills: List of extracted skill strings.
        :return: Tuple of match ratio and formatted match string.
        """
        count = 0

        for skill in extract_skills:
            if skill in main_skills:
                count += 1

        return count / len(main_skills), f"{count}/{len(main_skills)}"