Vincent Claes
commited on
Commit
•
237ec40
1
Parent(s):
a1372cb
jobfixers add matching score + css template)
Browse files- app.py +28 -38
- recruiting_assistant.py +2 -1
- scripts/scrape_website.py +9 -10
- skills.py +42 -2
app.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1 |
import json
|
2 |
import os
|
|
|
|
|
3 |
import gradio as gr
|
4 |
import requests
|
5 |
from langchain.chat_models import ChatOpenAI
|
@@ -17,7 +19,9 @@ def search(resume):
|
|
17 |
"Content-Type": "application/json",
|
18 |
"x-api-key": os.environ["API_KEY"],
|
19 |
} # pas headers indien nodig aan
|
20 |
-
response = requests.post(
|
|
|
|
|
21 |
response_data = response.json()
|
22 |
|
23 |
if "prediction" in response_data:
|
@@ -27,46 +31,32 @@ def search(resume):
|
|
27 |
html_table = "<table>"
|
28 |
|
29 |
# Add table headers
|
30 |
-
html_table += "<tr><th>Vacancy</th><th>Score</th><th>Skills Match</th
|
31 |
-
|
32 |
-
for i, vacancy in enumerate(prediction):
|
33 |
-
(
|
34 |
-
score,
|
35 |
-
skills_intersection,
|
36 |
-
skills_not_in_intersection,
|
37 |
-
) = get_skills_score(vacancy=vacancy, resume=resume)
|
38 |
-
# Voeg een nieuwe regel toe na elke '.' en voeg "Vacature <volgnummer>:\n" toe voor elk item
|
39 |
-
updated_item = f"VACATURE {i + 1}: {vacancy}"
|
40 |
-
html_table += f"<tr><td>{updated_item}</td><td>{score}</td><td>{skills_intersection}</td><td>{skills_not_in_intersection}</td></tr>"
|
41 |
-
html_table += "</table>"
|
42 |
-
return html_table
|
43 |
|
44 |
-
|
|
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
skills_intersection = utils.get_intersection(
|
59 |
-
vacancy_skills_predicted, resume_skills_predicted
|
60 |
-
)
|
61 |
-
skills_not_in_intersection = utils.not_in_intersection(
|
62 |
-
vacancy_skills_predicted, resume_skills_predicted
|
63 |
-
)
|
64 |
-
match_score = utils.match(
|
65 |
-
present_features=skills_intersection,
|
66 |
-
not_present_features=skills_not_in_intersection,
|
67 |
-
)
|
68 |
-
return match_score, skills_intersection, skills_not_in_intersection
|
69 |
|
|
|
70 |
|
71 |
examples = [
|
72 |
"""
|
@@ -116,7 +106,7 @@ examples = [
|
|
116 |
"""
|
117 |
]
|
118 |
|
119 |
-
demo = gr.Blocks()
|
120 |
|
121 |
with demo:
|
122 |
with gr.Group():
|
|
|
1 |
import json
|
2 |
import os
|
3 |
+
from concurrent.futures import ThreadPoolExecutor
|
4 |
+
|
5 |
import gradio as gr
|
6 |
import requests
|
7 |
from langchain.chat_models import ChatOpenAI
|
|
|
19 |
"Content-Type": "application/json",
|
20 |
"x-api-key": os.environ["API_KEY"],
|
21 |
} # pas headers indien nodig aan
|
22 |
+
response = requests.post(
|
23 |
+
url, headers=headers, data=json.dumps({"text": resume, "limit": 7})
|
24 |
+
)
|
25 |
response_data = response.json()
|
26 |
|
27 |
if "prediction" in response_data:
|
|
|
31 |
html_table = "<table>"
|
32 |
|
33 |
# Add table headers
|
34 |
+
html_table += "<tr><th>Vacancy</th><th>Score</th><th>Skills Match</th></tr>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
+
# Prepare a list to hold the futures
|
37 |
+
futures = []
|
38 |
|
39 |
+
# Create a ThreadPoolExecutor
|
40 |
+
with ThreadPoolExecutor() as executor:
|
41 |
+
for i, vacancy in enumerate(prediction):
|
42 |
+
# Schedule the get_skills_match function to run and store the future
|
43 |
+
future = executor.submit(
|
44 |
+
skills.get_skills_match, llm, vacancy, resume
|
45 |
+
)
|
46 |
+
futures.append((i, vacancy, future))
|
47 |
|
48 |
+
# Collect the results as they become available
|
49 |
+
for i, vacancy, future in futures:
|
50 |
+
skills_match = future.result()
|
51 |
+
skills_match_predicted = utils.get_json_list_from_result(
|
52 |
+
skills_match, "skills_match_predicted"
|
53 |
+
)
|
54 |
+
updated_item = f"VACATURE {i + 1}: {vacancy}"
|
55 |
+
html_table += f"<tr><td>{updated_item}</td><td>{len(skills_match_predicted)}</td><td>{skills_match_predicted}</td></tr>"
|
56 |
+
html_table += "</table>"
|
57 |
+
return html_table
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
return "niets teruggevonden ..."
|
60 |
|
61 |
examples = [
|
62 |
"""
|
|
|
106 |
"""
|
107 |
]
|
108 |
|
109 |
+
demo = gr.Blocks(theme=gr.themes.Soft())
|
110 |
|
111 |
with demo:
|
112 |
with gr.Group():
|
recruiting_assistant.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
import json
|
2 |
import os
|
|
|
|
|
3 |
from langchain.chat_models import ChatOpenAI
|
4 |
from langchain.prompts import ChatPromptTemplate
|
5 |
-
from langchain.chains import LLMChain, SequentialChain
|
6 |
|
7 |
llm = ChatOpenAI(temperature=0.0, openai_api_key=os.environ["OPENAI"])
|
8 |
|
|
|
1 |
import json
|
2 |
import os
|
3 |
+
|
4 |
+
from langchain.chains import LLMChain, SequentialChain
|
5 |
from langchain.chat_models import ChatOpenAI
|
6 |
from langchain.prompts import ChatPromptTemplate
|
|
|
7 |
|
8 |
llm = ChatOpenAI(temperature=0.0, openai_api_key=os.environ["OPENAI"])
|
9 |
|
scripts/scrape_website.py
CHANGED
@@ -1,21 +1,20 @@
|
|
|
|
1 |
import os
|
|
|
|
|
|
|
2 |
import openai
|
3 |
import pandas as pd
|
4 |
-
import
|
5 |
from selenium import webdriver
|
6 |
-
from selenium.webdriver.chrome.service import Service
|
7 |
-
from webdriver_manager.chrome import ChromeDriverManager
|
8 |
-
from selenium.webdriver.common.by import By
|
9 |
from selenium.common.exceptions import (
|
10 |
-
NoSuchElementException,
|
11 |
-
TimeoutException,
|
12 |
WebDriverException,
|
13 |
)
|
14 |
-
from selenium.webdriver.
|
|
|
15 |
from selenium.webdriver.support import expected_conditions as EC
|
16 |
-
from
|
17 |
-
from
|
18 |
-
import time
|
19 |
|
20 |
# Set up OpenAI API
|
21 |
openai.api_key = os.getenv("OPENAI")
|
|
|
1 |
+
import json
|
2 |
import os
|
3 |
+
import time
|
4 |
+
from urllib.parse import urlparse
|
5 |
+
|
6 |
import openai
|
7 |
import pandas as pd
|
8 |
+
from bs4 import BeautifulSoup
|
9 |
from selenium import webdriver
|
|
|
|
|
|
|
10 |
from selenium.common.exceptions import (
|
|
|
|
|
11 |
WebDriverException,
|
12 |
)
|
13 |
+
from selenium.webdriver.chrome.service import Service
|
14 |
+
from selenium.webdriver.common.by import By
|
15 |
from selenium.webdriver.support import expected_conditions as EC
|
16 |
+
from selenium.webdriver.support.ui import WebDriverWait
|
17 |
+
from webdriver_manager.chrome import ChromeDriverManager
|
|
|
18 |
|
19 |
# Set up OpenAI API
|
20 |
openai.api_key = os.getenv("OPENAI")
|
skills.py
CHANGED
@@ -5,7 +5,8 @@ from langchain.prompts import ChatPromptTemplate
|
|
5 |
|
6 |
def get_vacancy_skills_chain(llm) -> LLMChain:
|
7 |
template_vacancy_get_skills = """
|
8 |
-
Given the following vacancy delimited by three backticks, retrieve the skills
|
|
|
9 |
Return the skills as a JSON list on 1 line, do not add newlines or any other text.
|
10 |
|
11 |
```
|
@@ -24,7 +25,8 @@ def get_vacancy_skills_chain(llm) -> LLMChain:
|
|
24 |
|
25 |
def get_resume_skills_chain(llm) -> LLMChain:
|
26 |
template_resume_skills = """
|
27 |
-
Given the following resume delimited by three backticks, retrieve the skills
|
|
|
28 |
Return the skills as a JSON list on 1 line, do not add newlines or any other text.
|
29 |
|
30 |
```
|
@@ -95,3 +97,41 @@ def get_skills_chain(llm) -> SequentialChain:
|
|
95 |
],
|
96 |
verbose=False,
|
97 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
def get_vacancy_skills_chain(llm) -> LLMChain:
|
7 |
template_vacancy_get_skills = """
|
8 |
+
Given the following vacancy delimited by three backticks, retrieve the skills requested in the vacancy.
|
9 |
+
Describe the skills in preferably 1 word and maximum 3 words.
|
10 |
Return the skills as a JSON list on 1 line, do not add newlines or any other text.
|
11 |
|
12 |
```
|
|
|
25 |
|
26 |
def get_resume_skills_chain(llm) -> LLMChain:
|
27 |
template_resume_skills = """
|
28 |
+
Given the following resume delimited by three backticks, retrieve the skills from the resume.
|
29 |
+
Describe the skills in preferably 1 word and maximum 3 words.
|
30 |
Return the skills as a JSON list on 1 line, do not add newlines or any other text.
|
31 |
|
32 |
```
|
|
|
97 |
],
|
98 |
verbose=False,
|
99 |
)
|
100 |
+
|
101 |
+
|
102 |
+
def get_skills_match(llm, vacancy, resume) -> SequentialChain:
|
103 |
+
|
104 |
+
template_get_skills_intersection = """
|
105 |
+
|
106 |
+
```
|
107 |
+
{vacancy}
|
108 |
+
```
|
109 |
+
|
110 |
+
Can you list the matches between the vacancy above delimited by three backticks with the resume below delimited by three backticks.
|
111 |
+
Consider skills that are not exact but are close to each other in terms of meaning or usage, or that are related or in the neighbourhood.
|
112 |
+
If no skills match do not make up a response and return an empty list.
|
113 |
+
Return the matches as a JSON list on 1 line, do not add newlines or any other text.
|
114 |
+
|
115 |
+
```
|
116 |
+
{resume}
|
117 |
+
```
|
118 |
+
"""
|
119 |
+
|
120 |
+
prompt_get_skills_intersection = ChatPromptTemplate.from_template(
|
121 |
+
template=template_get_skills_intersection
|
122 |
+
)
|
123 |
+
skills_match_chain = LLMChain(
|
124 |
+
llm=llm,
|
125 |
+
prompt=prompt_get_skills_intersection,
|
126 |
+
output_key="skills_match_predicted",
|
127 |
+
)
|
128 |
+
|
129 |
+
chain = SequentialChain(
|
130 |
+
chains=[skills_match_chain],
|
131 |
+
input_variables=["vacancy", "resume"],
|
132 |
+
output_variables=[
|
133 |
+
skills_match_chain.output_key,
|
134 |
+
],
|
135 |
+
verbose=False,
|
136 |
+
)
|
137 |
+
return chain({"vacancy": vacancy, "resume": resume})
|