mbosse99 commited on
Commit
7a876be
·
1 Parent(s): 2f94940

Update with filters and mail dispatch

Browse files
Files changed (1) hide show
  1. app.py +288 -108
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
- if not st.session_state["job"] and submit:
112
- st.error("Please upload a job description to search for candidates")
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
- raw_candidates = st.session_state["db"].similarity_search(query_string, k=candidates_per_search+current_offset)
 
 
 
 
 
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
- # print("ich bin im spinner")
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["source"]):
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["source"] for candidate in st.session_state["final_candidates"]))
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.success('Successful search for candidates and generation of questions')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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