Spaces:
Sleeping
Sleeping
Update with filters and mail dispatch
Browse files
app.py
CHANGED
@@ -3,17 +3,154 @@ import os
|
|
3 |
import openai
|
4 |
import re
|
5 |
import sqlite3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
import streamlit as st
|
7 |
from streamlit_js_eval import streamlit_js_eval
|
8 |
from langchain.embeddings.openai import OpenAIEmbeddings
|
9 |
from langchain.vectorstores.azuresearch import AzureSearch
|
|
|
|
|
10 |
from PyPDF2 import PdfReader
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
openai.api_key = os.getenv("OPENAI_API_KEY")
|
13 |
openai.api_base = "https://tensora-oai.openai.azure.com/"
|
14 |
openai.api_type = "azure"
|
15 |
openai.api_version = "2023-05-15"
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
st.markdown(
|
18 |
"""
|
19 |
<style>
|
@@ -37,6 +174,33 @@ with open("sys_prompt_frontend.txt") as f:
|
|
37 |
def adjust_numbering(lst):
|
38 |
return [f"{i + 1}. {item.split('. ', 1)[1]}" for i, item in enumerate(lst)]
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
def check_keywords_in_content(database_path, table_name, input_id, keywords):
|
41 |
# Verbindung zur Datenbank herstellen
|
42 |
conn = sqlite3.connect(database_path)
|
@@ -81,12 +245,12 @@ if "db" not in st.session_state:
|
|
81 |
embedder = OpenAIEmbeddings(deployment="text-embedding-ada-002", chunk_size=1)
|
82 |
embedding_function = embedder.embed_query
|
83 |
|
84 |
-
|
85 |
db = AzureSearch(
|
86 |
-
index_name="wg-cvs",
|
87 |
azure_search_endpoint=os.environ.get("AZURE_SEARCH_ENDPOINT"),
|
88 |
azure_search_key=os.environ.get("AZURE_SEARCH_KEY"),
|
89 |
embedding_function=embedding_function,
|
|
|
90 |
)
|
91 |
st.session_state["db"] = db
|
92 |
|
@@ -105,13 +269,25 @@ with col_clear:
|
|
105 |
if st.button("Clear", use_container_width=True):
|
106 |
streamlit_js_eval(js_expressions="parent.window.location.reload()")
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
text_area_params = st.text_area(label="Add additional search parameters, which are separated by commas (e.g. master, phd, web developer, spanish)")
|
109 |
|
110 |
submit = st.button("Search candidates",disabled= True if st.session_state["final_candidates"] else False)
|
111 |
-
|
112 |
-
|
113 |
-
if st.session_state["docs_res"] and submit:
|
114 |
with st.spinner("Load the candidates, this may take a moment..."):
|
|
|
115 |
query_string = "The following keywords must be included: " + text_area_params + " " + st.session_state["job_string"]
|
116 |
checked_candidates = []
|
117 |
db_path = 'cvdb.db'
|
@@ -120,9 +296,27 @@ if st.session_state["docs_res"] and submit:
|
|
120 |
target_candidates_count = 10
|
121 |
current_offset = 0
|
122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
while len(checked_candidates) < target_candidates_count:
|
124 |
# Führe eine similarity search durch und erhalte 100 Kandidaten
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
for candidate in raw_candidates[current_offset:]:
|
128 |
candidates_id = candidate.metadata["source"].split("/")[-1]
|
@@ -138,11 +332,15 @@ if st.session_state["docs_res"] and submit:
|
|
138 |
current_offset += candidates_per_search
|
139 |
if current_offset == 600:
|
140 |
break
|
141 |
-
|
142 |
# Setze die Ergebnisse in der Session State Variable
|
143 |
st.session_state["docs_res"] = checked_candidates
|
144 |
if len(checked_candidates) == 0:
|
145 |
st.error("No candidates can be found with these keywords. Please adjust the keywords and try again.", icon="🚨")
|
|
|
|
|
|
|
|
|
|
|
146 |
if (st.session_state["job"] and submit) or st.session_state["docs_res"]:
|
147 |
if not st.session_state["job_string"]:
|
148 |
pdf_data_jobdescription = st.session_state["job"].read()
|
@@ -154,104 +352,7 @@ if (st.session_state["job"] and submit) or st.session_state["docs_res"]:
|
|
154 |
# st.session_state["pdf_data_jobdescription"] = pdf_data_jobdescription activate and add sessio state if data is needed
|
155 |
st.session_state["job_string"] = pdf_data_jobdescription_string
|
156 |
if not st.session_state["docs_res"]:
|
157 |
-
|
158 |
-
# print(st.session_state["job_string"]+" "+text_area_params)
|
159 |
-
with st.spinner("Load the candidates, this may take a moment..."):
|
160 |
-
#Use this line if you just want to perform one similarity search
|
161 |
-
# st.session_state["docs_res"] = st.session_state["db"].similarity_search(text_area_params+" "+st.session_state["job_string"], k=100)
|
162 |
-
|
163 |
-
query_string = "The following keywords must be included: " + text_area_params + " " + st.session_state["job_string"]
|
164 |
-
checked_candidates = []
|
165 |
-
db_path = 'cvdb.db'
|
166 |
-
table_name = 'files'
|
167 |
-
candidates_per_search = 100
|
168 |
-
target_candidates_count = 10
|
169 |
-
current_offset = 0
|
170 |
-
|
171 |
-
while len(checked_candidates) < target_candidates_count:
|
172 |
-
# Führe eine similarity search durch und erhalte 100 Kandidaten
|
173 |
-
raw_candidates = st.session_state["db"].similarity_search(query_string, k=candidates_per_search+current_offset)
|
174 |
-
|
175 |
-
for candidate in raw_candidates[current_offset:]:
|
176 |
-
candidates_id = candidate.metadata["source"].split("/")[-1]
|
177 |
-
keyword_bool = check_keywords_in_content(db_path, table_name, candidates_id, text_area_params.split(','))
|
178 |
-
|
179 |
-
if keyword_bool:
|
180 |
-
checked_candidates.append(candidate)
|
181 |
-
|
182 |
-
# Überprüfe, ob die Zielanzahl erreicht wurde und breche die Schleife ab, wenn ja
|
183 |
-
if len(checked_candidates) >= target_candidates_count:
|
184 |
-
break
|
185 |
-
|
186 |
-
current_offset += candidates_per_search
|
187 |
-
if current_offset == 600:
|
188 |
-
break
|
189 |
-
|
190 |
-
# Setze die Ergebnisse in der Session State Variable
|
191 |
-
st.session_state["docs_res"] = checked_candidates
|
192 |
-
if len(checked_candidates) == 0:
|
193 |
-
st.error("No candidates can be found with these keywords. Please adjust the keywords and try again.", icon="🚨")
|
194 |
-
# query_string = "The following keywords must be included: "+text_area_params+" "+st.session_state["job_string"]
|
195 |
-
# raw_candidates = st.session_state["db"].similarity_search(query_string, k=100)
|
196 |
-
# checked_candidates = []
|
197 |
-
# db_path = 'cvdb.db'
|
198 |
-
# table_name = 'files'
|
199 |
-
# for candidate in raw_candidates:
|
200 |
-
# candidates_id = candidate.metadata["source"].split("/")[-1]
|
201 |
-
# keyword_bool = check_keywords_in_content(db_path,table_name,candidates_id,text_area_params.split(','))
|
202 |
-
# print(keyword_bool)
|
203 |
-
# if check_keywords_in_content(db_path,table_name,candidates_id,text_area_params.split(',')):
|
204 |
-
# if len(checked_candidates)<15:
|
205 |
-
# checked_candidates.append(candidate)
|
206 |
-
# else:
|
207 |
-
# break
|
208 |
-
|
209 |
-
# st.session_state["docs_res"] = checked_candidates
|
210 |
-
#This Code is creating a new Index based on the raw candidates
|
211 |
-
# raw_candidates = st.session_state["db"].similarity_search(text_area_params+" "+st.session_state["job_string"], k=50)
|
212 |
-
# raw_candidates_embeddings = []
|
213 |
-
# for candidate in raw_candidates:
|
214 |
-
# raw_candidates_embeddings.append(embedding_function(candidate.page_content))
|
215 |
-
|
216 |
-
# st.session_state["docs_res"] = st.session_state["db"].similarity_search_by_vector(embedding=raw_candidates_embeddings,k=10,query="Every candidate needs to be proficient in spanish")
|
217 |
-
# db_temp = AzureSearch.from_documents(
|
218 |
-
# raw_candidates,
|
219 |
-
# embedding=embedder,
|
220 |
-
# index_name="wg-cvs-temp",
|
221 |
-
# azure_search_endpoint=os.environ.get("AZURE_SEARCH_ENDPOINT"),
|
222 |
-
# azure_search_key=os.environ.get("AZURE_SEARCH_KEY"),
|
223 |
-
# )
|
224 |
-
|
225 |
-
# st.session_state["docs_res"] = db_temp.similarity_search(query="Every candidate needs to be proficient in spanish", k=10)
|
226 |
-
|
227 |
-
#Use this code to check candidates with gpt-4
|
228 |
-
# raw_candidates = st.session_state["db"].similarity_search(text_area_params+" "+st.session_state["job_string"], k=15)
|
229 |
-
# temp_candidates = []
|
230 |
-
# for candidate in raw_candidates:
|
231 |
-
# res_approve = openai.ChatCompletion.create(
|
232 |
-
# engine="gpt-4",
|
233 |
-
# temperature=0.1,
|
234 |
-
# messages=[
|
235 |
-
# {
|
236 |
-
# "role": "system",
|
237 |
-
# "content": "You are a professional recruiter who receives a resume and a set of requirements. The only thing you have to do is to say whether the requirements are fulfilled or not. you should not explain yourself and simply answer '1' if the requirements are fulfilled and '0' if not.",
|
238 |
-
# },
|
239 |
-
# {
|
240 |
-
# "role": "system",
|
241 |
-
# "content": "The candidate needs to be located in New York"
|
242 |
-
# },
|
243 |
-
# {
|
244 |
-
# "role": "system",
|
245 |
-
# "content": candidate.page_content
|
246 |
-
# }
|
247 |
-
# ],
|
248 |
-
# )
|
249 |
-
# print(res_approve.choices[0]["message"]["content"])
|
250 |
-
# if res_approve.choices[0]["message"]["content"] == "1":
|
251 |
-
# temp_candidates.append(candidate)
|
252 |
-
# st.session_state["docs_res"] = temp_candidates
|
253 |
-
|
254 |
-
|
255 |
if not st.session_state["final_candidates"]:
|
256 |
for i,doc in enumerate(st.session_state["docs_res"]):
|
257 |
# print(doc)
|
@@ -263,7 +364,7 @@ if (st.session_state["job"] and submit) or st.session_state["docs_res"]:
|
|
263 |
st.rerun()
|
264 |
with cols_final[0]:
|
265 |
# st.subheader(doc.metadata["source"])
|
266 |
-
with st.expander(doc.metadata["
|
267 |
st.write(doc.page_content)
|
268 |
if st.button("Accept candidates", key="accept_candidates_btn"):
|
269 |
print("hello")
|
@@ -272,7 +373,7 @@ if (st.session_state["job"] and submit) or st.session_state["docs_res"]:
|
|
272 |
else:
|
273 |
print("Now Questions")
|
274 |
st.subheader("Your Candidates:")
|
275 |
-
st.write(", ".join(candidate.metadata["
|
276 |
# for i,candidate in enumerate(st.session_state["final_candidates"]):
|
277 |
# st.write(candidate.metadata["source"])
|
278 |
cv_strings = "; Next CV: ".join(candidate.page_content for candidate in st.session_state["final_candidates"])
|
@@ -350,7 +451,86 @@ if (st.session_state["job"] and submit) or st.session_state["docs_res"]:
|
|
350 |
with cols_final[0]:
|
351 |
st.write(final_q)
|
352 |
if len(st.session_state["final_question_string"])>0:
|
|
|
|
|
353 |
if st.button("Submit", use_container_width=True):
|
354 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
355 |
|
356 |
|
|
|
3 |
import openai
|
4 |
import re
|
5 |
import sqlite3
|
6 |
+
import base64
|
7 |
+
import calendar
|
8 |
+
import json
|
9 |
+
import time
|
10 |
+
import uuid
|
11 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph
|
12 |
+
from reportlab.lib.styles import getSampleStyleSheet
|
13 |
import streamlit as st
|
14 |
from streamlit_js_eval import streamlit_js_eval
|
15 |
from langchain.embeddings.openai import OpenAIEmbeddings
|
16 |
from langchain.vectorstores.azuresearch import AzureSearch
|
17 |
+
from azure.storage.blob import BlobServiceClient
|
18 |
+
from azure.cosmos import CosmosClient, exceptions
|
19 |
from PyPDF2 import PdfReader
|
20 |
+
import openai
|
21 |
+
import sendgrid
|
22 |
+
from sendgrid.helpers.mail import Mail, Attachment, FileContent, FileName, FileType, Disposition
|
23 |
+
import ssl
|
24 |
+
ssl._create_default_https_context = ssl._create_unverified_context
|
25 |
|
26 |
openai.api_key = os.getenv("OPENAI_API_KEY")
|
27 |
openai.api_base = "https://tensora-oai.openai.azure.com/"
|
28 |
openai.api_type = "azure"
|
29 |
openai.api_version = "2023-05-15"
|
30 |
|
31 |
+
connection_string = os.getenv("CONNECTION")
|
32 |
+
blob_service_client = BlobServiceClient.from_connection_string(connection_string)
|
33 |
+
|
34 |
+
def upload_blob(pdf_name, json_data, pdf_data_jobdescription,pdf_data_cvs, pre_generated_bool, custom_questions):
|
35 |
+
try:
|
36 |
+
container_name = "jobdescriptions"
|
37 |
+
# json_blob_name = f"{pdf_name}_jsondata.json"
|
38 |
+
pdf_blob_name_jobdescription = f"{pdf_name}.pdf"
|
39 |
+
|
40 |
+
container_client = blob_service_client.get_container_client(container_name)
|
41 |
+
|
42 |
+
# json_blob_client = container_client.get_blob_client(json_blob_name)
|
43 |
+
# json_blob_client.upload_blob(json_data.encode('utf-8'), overwrite=True)
|
44 |
+
|
45 |
+
pdf_blob_client = container_client.get_blob_client(pdf_blob_name_jobdescription)
|
46 |
+
pdf_blob_client.upload_blob(pdf_data_jobdescription, overwrite=True)
|
47 |
+
|
48 |
+
upload_job_db_item(pdf_name,len(pdf_data_cvs),json.loads(json_data),pre_generated_bool, custom_questions)
|
49 |
+
if pre_generated_bool:
|
50 |
+
for i,question in enumerate(custom_questions):
|
51 |
+
question_nr_for_id = i+1
|
52 |
+
question_id = pdf_name + "-question-nr-" + str(question_nr_for_id)+str(calendar.timegm(time.gmtime()))
|
53 |
+
upload_question_db_item(question_id, pdf_name, question,st.session_state["job_string"])
|
54 |
+
links = []
|
55 |
+
names = []
|
56 |
+
for i,cv in enumerate(pdf_data_cvs):
|
57 |
+
|
58 |
+
cv_nr_for_id = i+1
|
59 |
+
cv_session_state_string = "cv-"+str(cv_nr_for_id)
|
60 |
+
session_state_name = st.session_state["final_candidates"][i].metadata["name"]
|
61 |
+
names.append(session_state_name)
|
62 |
+
cv_id = pdf_name + "-cv-nr-" + str(cv_nr_for_id)+str(calendar.timegm(time.gmtime()))
|
63 |
+
upload_db_item(session_state_name, json.loads(json_data), pdf_name, cv_id)
|
64 |
+
pdf_blob_name_cv = f"{cv_id}.pdf"
|
65 |
+
pdf_blob_client = container_client.get_blob_client(pdf_blob_name_cv)
|
66 |
+
pdf_blob_client.upload_blob(pdf_data_cvs[i], overwrite=True)
|
67 |
+
links.append("https://tensora.ai/workgenius/cv-evaluation2/?job="+cv_id)
|
68 |
+
|
69 |
+
return links
|
70 |
+
except Exception as e:
|
71 |
+
print(f"Fehler beim Hochladen der Daten: {str(e)}")
|
72 |
+
return []
|
73 |
+
|
74 |
+
def upload_job_db_item(id, number_of_applicants, data, pre_generated_bool, custom_questions):
|
75 |
+
endpoint = "https://wg-candidate-data.documents.azure.com:443/"
|
76 |
+
key = os.getenv("CONNECTION_DB")
|
77 |
+
client = CosmosClient(endpoint, key)
|
78 |
+
database = client.get_database_client("ToDoList")
|
79 |
+
container = database.get_container_client("JobData")
|
80 |
+
job_item = {
|
81 |
+
"id": id,
|
82 |
+
'partitionKey' : 'wg-job-data-v1',
|
83 |
+
"title": data["title"],
|
84 |
+
"number_of_applicants": number_of_applicants,
|
85 |
+
"every_interview_conducted": False,
|
86 |
+
"evaluation_email": data["email"],
|
87 |
+
"question_one": data["question_one"],
|
88 |
+
"question_two": data["question_two"],
|
89 |
+
"question_three": data["question_three"],
|
90 |
+
"pre_generated": pre_generated_bool,
|
91 |
+
"custom_questions": custom_questions
|
92 |
+
}
|
93 |
+
try:
|
94 |
+
# Fügen Sie das Element in den Container ein
|
95 |
+
container.create_item(body=job_item)
|
96 |
+
print("Eintrag erfolgreich in die Cosmos DB eingefügt. Container: Job Data")
|
97 |
+
except exceptions.CosmosHttpResponseError as e:
|
98 |
+
print(f"Fehler beim Schreiben in die Cosmos DB: {str(e)}")
|
99 |
+
except Exception as e:
|
100 |
+
print(f"Allgemeiner Fehler: {str(e)}")
|
101 |
+
|
102 |
+
def upload_db_item(name, data, job_description_id, cv_id):
|
103 |
+
|
104 |
+
endpoint = "https://wg-candidate-data.documents.azure.com:443/"
|
105 |
+
key = os.getenv("CONNECTION_DB")
|
106 |
+
client = CosmosClient(endpoint, key)
|
107 |
+
database = client.get_database_client("ToDoList")
|
108 |
+
container = database.get_container_client("Items")
|
109 |
+
candidate_item = {
|
110 |
+
"id": cv_id,
|
111 |
+
'partitionKey' : 'wg-candidate-data-v1',
|
112 |
+
"name": name,
|
113 |
+
"title": data["title"],
|
114 |
+
"interview_conducted": False,
|
115 |
+
"ai_summary": "",
|
116 |
+
"evaluation_email": data["email"],
|
117 |
+
"question_one": data["question_one"],
|
118 |
+
"question_two": data["question_two"],
|
119 |
+
"question_three": data["question_three"],
|
120 |
+
"job_description_id": job_description_id,
|
121 |
+
}
|
122 |
+
|
123 |
+
try:
|
124 |
+
# Fügen Sie das Element in den Container ein
|
125 |
+
container.create_item(body=candidate_item)
|
126 |
+
print("Eintrag erfolgreich in die Cosmos DB eingefügt. Container: Items(candidate Data)")
|
127 |
+
except exceptions.CosmosHttpResponseError as e:
|
128 |
+
print(f"Fehler beim Schreiben in die Cosmos DB: {str(e)}")
|
129 |
+
except Exception as e:
|
130 |
+
print(f"Allgemeiner Fehler: {str(e)}")
|
131 |
+
|
132 |
+
def upload_question_db_item(id, job_id, question, job_content):
|
133 |
+
endpoint = "https://wg-candidate-data.documents.azure.com:443/"
|
134 |
+
key = os.getenv("CONNECTION_DB")
|
135 |
+
client = CosmosClient(endpoint, key)
|
136 |
+
database = client.get_database_client("ToDoList")
|
137 |
+
container = database.get_container_client("Questions")
|
138 |
+
question_item = {
|
139 |
+
"id": id,
|
140 |
+
"partitionKey" : "wg-question-data-v1",
|
141 |
+
"job_id": job_id,
|
142 |
+
"question_content": question,
|
143 |
+
"job_description": job_content,
|
144 |
+
}
|
145 |
+
try:
|
146 |
+
# Fügen Sie das Element in den Container ein
|
147 |
+
container.create_item(body=question_item)
|
148 |
+
print("Eintrag erfolgreich in die Cosmos DB eingefügt. Container: Questions(Question Data)")
|
149 |
+
except exceptions.CosmosHttpResponseError as e:
|
150 |
+
print(f"Fehler beim Schreiben in die Cosmos DB: {str(e)}")
|
151 |
+
except Exception as e:
|
152 |
+
print(f"Allgemeiner Fehler: {str(e)}")
|
153 |
+
|
154 |
st.markdown(
|
155 |
"""
|
156 |
<style>
|
|
|
174 |
def adjust_numbering(lst):
|
175 |
return [f"{i + 1}. {item.split('. ', 1)[1]}" for i, item in enumerate(lst)]
|
176 |
|
177 |
+
def generate_candidate_mail(candidate, chat_link)-> str:
|
178 |
+
prompt = "You are a professional recruiter who has selected a suitable candidate based on a job description. Your task is to write two to three sentences about the candidate and why we think they are suitable for the job. The text will then be used in an email to the candidate, so address the candidate."
|
179 |
+
res = openai.ChatCompletion.create(
|
180 |
+
engine="gpt-4",
|
181 |
+
temperature=0.2,
|
182 |
+
messages=[
|
183 |
+
{
|
184 |
+
"role": "system",
|
185 |
+
"content": prompt,
|
186 |
+
},
|
187 |
+
{"role": "system", "content": "Job description: "+st.session_state["job_string"]+"; Resume: "+candidate.page_content}
|
188 |
+
],
|
189 |
+
)
|
190 |
+
# print(res.choices[0]["message"]["content"])
|
191 |
+
output_string = f"""{res.choices[0]["message"]["content"]}
|
192 |
+
|
193 |
+
We have added the job description to the mail attachment.
|
194 |
+
If you are interested in the position, please click on the following link, answer a few questions from our chatbot for about 10-15 minutes and we will get back to you.
|
195 |
+
|
196 |
+
Link to the interview chatbot: {chat_link}
|
197 |
+
|
198 |
+
Sincerely,
|
199 |
+
WorkGenius
|
200 |
+
"""
|
201 |
+
print("Mail generated")
|
202 |
+
return output_string
|
203 |
+
|
204 |
def check_keywords_in_content(database_path, table_name, input_id, keywords):
|
205 |
# Verbindung zur Datenbank herstellen
|
206 |
conn = sqlite3.connect(database_path)
|
|
|
245 |
embedder = OpenAIEmbeddings(deployment="text-embedding-ada-002", chunk_size=1)
|
246 |
embedding_function = embedder.embed_query
|
247 |
|
|
|
248 |
db = AzureSearch(
|
249 |
+
index_name="wg-cvs-data",
|
250 |
azure_search_endpoint=os.environ.get("AZURE_SEARCH_ENDPOINT"),
|
251 |
azure_search_key=os.environ.get("AZURE_SEARCH_KEY"),
|
252 |
embedding_function=embedding_function,
|
253 |
+
# fields=fields
|
254 |
)
|
255 |
st.session_state["db"] = db
|
256 |
|
|
|
269 |
if st.button("Clear", use_container_width=True):
|
270 |
streamlit_js_eval(js_expressions="parent.window.location.reload()")
|
271 |
|
272 |
+
st.write("Switch from a similarity search (default) to a hybrid search (activated)")
|
273 |
+
st.toggle("Switch Search", key="search_type")
|
274 |
+
|
275 |
+
st.write("Activate the following toggles to filter according to the respective properties:")
|
276 |
+
col_screening, col_handoff, col_placed = st.columns([1,1,1])
|
277 |
+
with col_screening:
|
278 |
+
st.toggle("Screened", key="screened")
|
279 |
+
with col_handoff:
|
280 |
+
st.toggle("Handed over", key="handed")
|
281 |
+
with col_placed:
|
282 |
+
st.toggle("Placed", key="placed")
|
283 |
+
|
284 |
text_area_params = st.text_area(label="Add additional search parameters, which are separated by commas (e.g. master, phd, web developer, spanish)")
|
285 |
|
286 |
submit = st.button("Search candidates",disabled= True if st.session_state["final_candidates"] else False)
|
287 |
+
|
288 |
+
def load_candidates():
|
|
|
289 |
with st.spinner("Load the candidates, this may take a moment..."):
|
290 |
+
filter_string = ""
|
291 |
query_string = "The following keywords must be included: " + text_area_params + " " + st.session_state["job_string"]
|
292 |
checked_candidates = []
|
293 |
db_path = 'cvdb.db'
|
|
|
296 |
target_candidates_count = 10
|
297 |
current_offset = 0
|
298 |
|
299 |
+
if st.session_state["screened"]:
|
300 |
+
filter_string = "amount_screenings gt 0 "
|
301 |
+
if st.session_state["handed"]:
|
302 |
+
if len(filter_string) > 0:
|
303 |
+
filter_string += "and amount_handoffs gt 0 "
|
304 |
+
else:
|
305 |
+
filter_string += "amount_handoffs gt 0 "
|
306 |
+
if st.session_state["placed"]:
|
307 |
+
if len(filter_string) > 0:
|
308 |
+
filter_string += "and amount_placed gt 0"
|
309 |
+
else:
|
310 |
+
filter_string += "amount_placed gt 0"
|
311 |
+
print(filter_string)
|
312 |
while len(checked_candidates) < target_candidates_count:
|
313 |
# Führe eine similarity search durch und erhalte 100 Kandidaten
|
314 |
+
if st.session_state["search_type"]:
|
315 |
+
print("hybrid")
|
316 |
+
raw_candidates = st.session_state["db"].hybrid_search(query_string, k=candidates_per_search+current_offset, filters=filter_string)
|
317 |
+
else:
|
318 |
+
print("similarity")
|
319 |
+
raw_candidates = st.session_state["db"].similarity_search(query_string, k=candidates_per_search+current_offset, filters=filter_string)
|
320 |
|
321 |
for candidate in raw_candidates[current_offset:]:
|
322 |
candidates_id = candidate.metadata["source"].split("/")[-1]
|
|
|
332 |
current_offset += candidates_per_search
|
333 |
if current_offset == 600:
|
334 |
break
|
|
|
335 |
# Setze die Ergebnisse in der Session State Variable
|
336 |
st.session_state["docs_res"] = checked_candidates
|
337 |
if len(checked_candidates) == 0:
|
338 |
st.error("No candidates can be found with these keywords. Please adjust the keywords and try again.", icon="🚨")
|
339 |
+
|
340 |
+
if not st.session_state["job"] and submit:
|
341 |
+
st.error("Please upload a job description to search for candidates")
|
342 |
+
if st.session_state["docs_res"] and submit:
|
343 |
+
load_candidates()
|
344 |
if (st.session_state["job"] and submit) or st.session_state["docs_res"]:
|
345 |
if not st.session_state["job_string"]:
|
346 |
pdf_data_jobdescription = st.session_state["job"].read()
|
|
|
352 |
# st.session_state["pdf_data_jobdescription"] = pdf_data_jobdescription activate and add sessio state if data is needed
|
353 |
st.session_state["job_string"] = pdf_data_jobdescription_string
|
354 |
if not st.session_state["docs_res"]:
|
355 |
+
load_candidates()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
356 |
if not st.session_state["final_candidates"]:
|
357 |
for i,doc in enumerate(st.session_state["docs_res"]):
|
358 |
# print(doc)
|
|
|
364 |
st.rerun()
|
365 |
with cols_final[0]:
|
366 |
# st.subheader(doc.metadata["source"])
|
367 |
+
with st.expander(doc.metadata["name"]):
|
368 |
st.write(doc.page_content)
|
369 |
if st.button("Accept candidates", key="accept_candidates_btn"):
|
370 |
print("hello")
|
|
|
373 |
else:
|
374 |
print("Now Questions")
|
375 |
st.subheader("Your Candidates:")
|
376 |
+
st.write(", ".join(candidate.metadata["name"] for candidate in st.session_state["final_candidates"]))
|
377 |
# for i,candidate in enumerate(st.session_state["final_candidates"]):
|
378 |
# st.write(candidate.metadata["source"])
|
379 |
cv_strings = "; Next CV: ".join(candidate.page_content for candidate in st.session_state["final_candidates"])
|
|
|
451 |
with cols_final[0]:
|
452 |
st.write(final_q)
|
453 |
if len(st.session_state["final_question_string"])>0:
|
454 |
+
st.text_input("Enter the email address to which the test emails should be sent:",key="recruiter_mail")
|
455 |
+
st.text_input("Enter the job title:", key="job_title")
|
456 |
if st.button("Submit", use_container_width=True):
|
457 |
+
with st.spinner("Generation and dispatch of mails. This process may take a few minutes..."):
|
458 |
+
sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API'))
|
459 |
+
# Sender- und Empfänger-E-Mail-Adressen
|
460 |
+
sender_email = "[email protected]"
|
461 |
+
receiver_email = st.session_state["recruiter_mail"]
|
462 |
+
print(receiver_email)
|
463 |
+
subject = "Mails for potential candidates for the following position: "+st.session_state["job_title"]
|
464 |
+
message = f"""Dear Recruiter,
|
465 |
+
|
466 |
+
enclosed in the text file you will find the e-mails that are sent to the potential candidates.
|
467 |
+
|
468 |
+
The subject of the mail would be the following: Are you interested in a new position as a {st.session_state["job_title"]}?
|
469 |
+
|
470 |
+
Sincerely,
|
471 |
+
Your Candidate-Search-Tool
|
472 |
+
"""
|
473 |
+
# SendGrid-E-Mail erstellen
|
474 |
+
message = Mail(
|
475 |
+
from_email=sender_email,
|
476 |
+
to_emails=receiver_email,
|
477 |
+
subject=subject,
|
478 |
+
plain_text_content=message,
|
479 |
+
)
|
480 |
+
data = {
|
481 |
+
"title": st.session_state["job_title"],
|
482 |
+
"email": st.session_state["recruiter_mail"],
|
483 |
+
"question_one": "",
|
484 |
+
"question_two": "",
|
485 |
+
"question_three": "",
|
486 |
+
}
|
487 |
+
json_data = json.dumps(data, ensure_ascii=False)
|
488 |
+
# Eine zufällige UUID generieren
|
489 |
+
random_uuid = uuid.uuid4()
|
490 |
+
|
491 |
+
# Die UUID als String darstellen
|
492 |
+
uuid_string = str(random_uuid)
|
493 |
+
|
494 |
+
pdf_name = uuid_string
|
495 |
+
cvs_data = []
|
496 |
+
temp_pdf_file = "candidate_pdf.pdf"
|
497 |
+
for candidate in st.session_state["final_candidates"]:
|
498 |
+
styles = getSampleStyleSheet()
|
499 |
+
pdf = SimpleDocTemplate(temp_pdf_file)
|
500 |
+
flowables = [Paragraph(candidate.page_content, styles['Normal'])]
|
501 |
+
pdf.build(flowables)
|
502 |
+
with open(temp_pdf_file, 'rb') as pdf_file:
|
503 |
+
bytes_data = pdf_file.read()
|
504 |
+
cvs_data.append(bytes_data)
|
505 |
+
os.remove(temp_pdf_file)
|
506 |
+
candidate_links = upload_blob(pdf_name, json_data, st.session_state["job"].read(),cvs_data,True,st.session_state["final_question_string"])
|
507 |
+
mail_txt_string = ""
|
508 |
+
for i, candidate in enumerate(st.session_state["final_candidates"]):
|
509 |
+
if i > 0:
|
510 |
+
mail_txt_string += "\n\nMail to the "+str(i+1)+". candidate: "+candidate.metadata["name"]+" "+candidate.metadata["candidateId"]+" \n\n"
|
511 |
+
else:
|
512 |
+
mail_txt_string += "Mail to the "+str(i+1)+". candidate: "+candidate.metadata["name"]+" "+candidate.metadata["candidateId"]+" \n\n"
|
513 |
+
mail_txt_string += generate_candidate_mail(candidate,candidate_links[i])
|
514 |
+
# Summary in eine TXT Datei schreiben
|
515 |
+
mail_txt_path = "mailattachment.txt"
|
516 |
+
with open(mail_txt_path, 'wb') as summary_file:
|
517 |
+
summary_file.write(mail_txt_string.encode('utf-8'))
|
518 |
+
# Resume als Anhang hinzufügen
|
519 |
+
with open(mail_txt_path, 'rb') as summary_file:
|
520 |
+
encode_file_summary = base64.b64encode(summary_file.read()).decode()
|
521 |
+
summary_attachment = Attachment()
|
522 |
+
summary_attachment.file_content = FileContent(encode_file_summary)
|
523 |
+
summary_attachment.file_name = FileName('candidate_mails.txt')
|
524 |
+
summary_attachment.file_type = FileType('text/plain')
|
525 |
+
summary_attachment.disposition = Disposition('attachment')
|
526 |
+
message.attachment = summary_attachment
|
527 |
+
try:
|
528 |
+
response = sg.send(message)
|
529 |
+
print("E-Mail wurde erfolgreich gesendet. Statuscode:", response.status_code)
|
530 |
+
st.success('The mail dispatch and the upload of the data was successful')
|
531 |
+
os.remove("mailattachment.txt")
|
532 |
+
except Exception as e:
|
533 |
+
print("Fehler beim Senden der E-Mail:", str(e))
|
534 |
+
st.error("Unfortunately the mail dispatch did not work. Please reload the page and try again or contact the administrator. ", icon="🚨")
|
535 |
|
536 |
|