Spaces:
Sleeping
Sleeping
Commit
·
fd5aa4d
1
Parent(s):
4b4bc5b
move ai part in new ai_manager.py
Browse files- .gitignore +1 -0
- WelcomeToTheJungle.py +1 -97
- ai_manager.py +98 -0
- jobspy_indeed.py +1 -98
- jobspy_linkedin.py +1 -98
.gitignore
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/__pycache__
|
WelcomeToTheJungle.py
CHANGED
@@ -2,107 +2,11 @@ import requests
|
|
2 |
import json
|
3 |
from datetime import datetime
|
4 |
import warnings
|
5 |
-
from mistralai import Mistral, SDKError
|
6 |
-
from time import sleep
|
7 |
from bs4 import BeautifulSoup
|
8 |
from markdownify import markdownify
|
|
|
9 |
|
10 |
warnings.filterwarnings("ignore")
|
11 |
-
import os
|
12 |
-
|
13 |
-
models = ["mistral-small-2409", "open-mistral-nemo"]
|
14 |
-
|
15 |
-
import random
|
16 |
-
def get_model():
|
17 |
-
return random.choice(models)
|
18 |
-
|
19 |
-
def call_ai(prompt, json_mode):
|
20 |
-
try:
|
21 |
-
return _call_ai(prompt, json_mode)
|
22 |
-
except SDKError as e:
|
23 |
-
#Wait, then try again once
|
24 |
-
sleep(11)
|
25 |
-
return _call_ai(prompt, json_mode)
|
26 |
-
except Exception as e:
|
27 |
-
# Throw the error if it's not an SDKError
|
28 |
-
raise
|
29 |
-
|
30 |
-
def _call_ai(prompt, json_mode):
|
31 |
-
sleep(1.1)
|
32 |
-
client = Mistral(api_key=os.getenv('MISTRAL_KEY'))
|
33 |
-
|
34 |
-
extra_param = {}
|
35 |
-
if json_mode:
|
36 |
-
extra_param = { "response_format" : {"type": "json_object"} }
|
37 |
-
|
38 |
-
chat_response = client.chat.complete(
|
39 |
-
model = get_model(),
|
40 |
-
messages = [
|
41 |
-
{
|
42 |
-
"role": "user",
|
43 |
-
"content": prompt,
|
44 |
-
},
|
45 |
-
],
|
46 |
-
**extra_param
|
47 |
-
)
|
48 |
-
|
49 |
-
return chat_response.choices[0].message.content
|
50 |
-
|
51 |
-
def get_offer_information(company, offer):
|
52 |
-
try:
|
53 |
-
return _get_offer_information(company, offer)
|
54 |
-
except json.decoder.JSONDecodeError as e:
|
55 |
-
#try again once
|
56 |
-
return _get_offer_information(company, offer)
|
57 |
-
except Exception as e:
|
58 |
-
# Throw the error if it's not an SDKError
|
59 |
-
raise
|
60 |
-
|
61 |
-
def _get_offer_information(company, offer):
|
62 |
-
prompt = """This is a job offer from the company '{}', make a JSON with this information:
|
63 |
-
- company_description (string): a description of the company in less than 15 words.
|
64 |
-
- position_summary (string): a summary of the role in 3 bullet points
|
65 |
-
- language_requirements (string): the language requirements in French and English
|
66 |
-
- experience_requirements (string): the experience requirements
|
67 |
-
- is_an_internship (Boolean): true if it's an internship, false otherwise
|
68 |
-
- salary_range (string): the salary range in yearly salary if stated, write 'unknown' otherwise
|
69 |
-
- should_apply (Boolean): True if the offer requires up to 2 years of work experience and does not ask for other languages than English, French, Hindi or Nepali
|
70 |
-
|
71 |
-
Be concise in each answer. Answer in English.
|
72 |
-
|
73 |
-
Example:
|
74 |
-
{{
|
75 |
-
'company_description': 'Galileo Global Education: A leading international network of higher education institutions.',
|
76 |
-
'position_summary': 'Project Manager Marketing and Communication: Develop brand experience, manage marketing/communication plan, ensure brand image, monitor e-reputation, create content, and collaborate with digital team.',
|
77 |
-
'language_requirements': 'French Fluent and English Native',
|
78 |
-
'experience_requirements': 'Previous experience in a similar role, preferably in an agency.',
|
79 |
-
'is_an_internship': false,
|
80 |
-
'salary_range': '€38,000-€42,000',
|
81 |
-
'should_apply': true,
|
82 |
-
}}
|
83 |
-
|
84 |
-
Offer:
|
85 |
-
{}""".format(company, offer)
|
86 |
-
result = call_ai(prompt, True)
|
87 |
-
obj = json.loads(result)
|
88 |
-
print(obj)
|
89 |
-
#Check result
|
90 |
-
if not "company_description" in obj:
|
91 |
-
obj["company_description"] = ""
|
92 |
-
if not "position_summary" in obj:
|
93 |
-
obj["position_summary"] = ""
|
94 |
-
if not "language_requirements" in obj:
|
95 |
-
obj["language_requirements"] = ""
|
96 |
-
if not "experience_requirements" in obj:
|
97 |
-
obj["experience_requirements"] = ""
|
98 |
-
if not "is_an_internship" in obj:
|
99 |
-
obj["is_an_internship"] = False
|
100 |
-
if not "salary_range" in obj:
|
101 |
-
obj["salary_range"] = ""
|
102 |
-
if not "should_apply" in obj:
|
103 |
-
obj["should_apply"] = True
|
104 |
-
|
105 |
-
return obj
|
106 |
|
107 |
def get_offer(url):
|
108 |
response = requests.get(url, verify=False)
|
|
|
2 |
import json
|
3 |
from datetime import datetime
|
4 |
import warnings
|
|
|
|
|
5 |
from bs4 import BeautifulSoup
|
6 |
from markdownify import markdownify
|
7 |
+
from ai_manager import get_offer_information
|
8 |
|
9 |
warnings.filterwarnings("ignore")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
def get_offer(url):
|
12 |
response = requests.get(url, verify=False)
|
ai_manager.py
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from mistralai import Mistral, SDKError
|
2 |
+
from time import sleep
|
3 |
+
import json
|
4 |
+
import os
|
5 |
+
|
6 |
+
models = ["mistral-small-2409", "open-mistral-nemo"]
|
7 |
+
|
8 |
+
import random
|
9 |
+
def get_model():
|
10 |
+
return random.choice(models)
|
11 |
+
|
12 |
+
def call_ai(prompt, json_mode):
|
13 |
+
try:
|
14 |
+
return _call_ai(prompt, json_mode)
|
15 |
+
except SDKError as e:
|
16 |
+
#Wait, then try again once
|
17 |
+
sleep(11)
|
18 |
+
return _call_ai(prompt, json_mode)
|
19 |
+
except Exception as e:
|
20 |
+
# Throw the error if it's not an SDKError
|
21 |
+
raise
|
22 |
+
|
23 |
+
def _call_ai(prompt, json_mode):
|
24 |
+
sleep(1.1)
|
25 |
+
client = Mistral(api_key=os.getenv('MISTRAL_KEY'))
|
26 |
+
|
27 |
+
extra_param = {}
|
28 |
+
if json_mode:
|
29 |
+
extra_param = { "response_format" : {"type": "json_object"} }
|
30 |
+
|
31 |
+
chat_response = client.chat.complete(
|
32 |
+
model = get_model(),
|
33 |
+
messages = [
|
34 |
+
{
|
35 |
+
"role": "user",
|
36 |
+
"content": prompt,
|
37 |
+
},
|
38 |
+
],
|
39 |
+
**extra_param
|
40 |
+
)
|
41 |
+
|
42 |
+
return chat_response.choices[0].message.content
|
43 |
+
|
44 |
+
def get_offer_information(company, offer):
|
45 |
+
try:
|
46 |
+
return _get_offer_information(company, offer)
|
47 |
+
except json.decoder.JSONDecodeError as e:
|
48 |
+
#try again once
|
49 |
+
return _get_offer_information(company, offer)
|
50 |
+
except Exception as e:
|
51 |
+
# Throw the error if it's not an SDKError
|
52 |
+
raise
|
53 |
+
|
54 |
+
def _get_offer_information(company, offer):
|
55 |
+
prompt = """This is a job offer from the company '{}', make a JSON with this information:
|
56 |
+
- company_description (string): a description of the company in less than 15 words.
|
57 |
+
- position_summary (string): a summary of the role in 3 bullet points
|
58 |
+
- language_requirements (string): the language requirements in French and English
|
59 |
+
- experience_requirements (string): the experience requirements
|
60 |
+
- is_an_internship (Boolean): true if it's an internship, false otherwise
|
61 |
+
- salary_range (string): the salary range in yearly salary if stated, write 'unknown' otherwise
|
62 |
+
- should_apply (Boolean): True if the offer requires up to 2 years of work experience and does not ask for other languages than English, French, Hindi or Nepali
|
63 |
+
|
64 |
+
Be concise in each answer. Answer in English.
|
65 |
+
|
66 |
+
Example:
|
67 |
+
{{
|
68 |
+
'company_description': 'Galileo Global Education: A leading international network of higher education institutions.',
|
69 |
+
'position_summary': 'Project Manager Marketing and Communication: Develop brand experience, manage marketing/communication plan, ensure brand image, monitor e-reputation, create content, and collaborate with digital team.',
|
70 |
+
'language_requirements': 'French Fluent and English Native',
|
71 |
+
'experience_requirements': 'Previous experience in a similar role, preferably in an agency.',
|
72 |
+
'is_an_internship': false,
|
73 |
+
'salary_range': '€38,000-€42,000',
|
74 |
+
'should_apply': true,
|
75 |
+
}}
|
76 |
+
|
77 |
+
Offer:
|
78 |
+
{}""".format(company, offer)
|
79 |
+
result = call_ai(prompt, True)
|
80 |
+
obj = json.loads(result)
|
81 |
+
print(obj)
|
82 |
+
#Check result
|
83 |
+
if not "company_description" in obj:
|
84 |
+
obj["company_description"] = ""
|
85 |
+
if not "position_summary" in obj:
|
86 |
+
obj["position_summary"] = ""
|
87 |
+
if not "language_requirements" in obj:
|
88 |
+
obj["language_requirements"] = ""
|
89 |
+
if not "experience_requirements" in obj:
|
90 |
+
obj["experience_requirements"] = ""
|
91 |
+
if not "is_an_internship" in obj:
|
92 |
+
obj["is_an_internship"] = False
|
93 |
+
if not "salary_range" in obj:
|
94 |
+
obj["salary_range"] = ""
|
95 |
+
if not "should_apply" in obj:
|
96 |
+
obj["should_apply"] = True
|
97 |
+
|
98 |
+
return obj
|
jobspy_indeed.py
CHANGED
@@ -1,106 +1,9 @@
|
|
1 |
-
import json
|
2 |
import warnings
|
3 |
-
from mistralai import Mistral, SDKError
|
4 |
-
from time import sleep
|
5 |
-
|
6 |
from jobspy import scrape_jobs
|
7 |
|
8 |
warnings.filterwarnings("ignore")
|
9 |
-
import os
|
10 |
-
|
11 |
-
models = ["mistral-small-2409", "open-mistral-nemo"]
|
12 |
-
|
13 |
-
import random
|
14 |
-
def get_model():
|
15 |
-
return random.choice(models)
|
16 |
-
|
17 |
-
def call_ai(prompt, json_mode):
|
18 |
-
try:
|
19 |
-
return _call_ai(prompt, json_mode)
|
20 |
-
except SDKError as e:
|
21 |
-
#Wait, then try again once
|
22 |
-
sleep(11)
|
23 |
-
return _call_ai(prompt, json_mode)
|
24 |
-
except Exception as e:
|
25 |
-
# Throw the error if it's not an SDKError
|
26 |
-
raise
|
27 |
-
|
28 |
-
def _call_ai(prompt, json_mode):
|
29 |
-
sleep(1.1)
|
30 |
-
client = Mistral(api_key=os.getenv('MISTRAL_KEY'))
|
31 |
-
|
32 |
-
extra_param = {}
|
33 |
-
if json_mode:
|
34 |
-
extra_param = { "response_format" : {"type": "json_object"} }
|
35 |
-
|
36 |
-
chat_response = client.chat.complete(
|
37 |
-
model = get_model(),
|
38 |
-
messages = [
|
39 |
-
{
|
40 |
-
"role": "user",
|
41 |
-
"content": prompt,
|
42 |
-
},
|
43 |
-
],
|
44 |
-
**extra_param
|
45 |
-
)
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
def get_offer_information(company, offer):
|
50 |
-
try:
|
51 |
-
return _get_offer_information(company, offer)
|
52 |
-
except json.decoder.JSONDecodeError as e:
|
53 |
-
#try again once
|
54 |
-
return _get_offer_information(company, offer)
|
55 |
-
except Exception as e:
|
56 |
-
# Throw the error if it's not an SDKError
|
57 |
-
raise
|
58 |
-
|
59 |
-
def _get_offer_information(company, offer):
|
60 |
-
prompt = """This is a job offer from the company '{}', make a JSON with this information:
|
61 |
-
- company_description (string): a description of the company in less than 15 words.
|
62 |
-
- position_summary (string): a summary of the role in 3 bullet points
|
63 |
-
- language_requirements (string): the language requirements in French and English
|
64 |
-
- experience_requirements (string): the experience requirements
|
65 |
-
- is_an_internship (Boolean): true if it's an internship, false otherwise
|
66 |
-
- salary_range (string): the salary range in yearly salary if stated, write 'unknown' otherwise
|
67 |
-
- should_apply (Boolean): True if the offer requires up to 2 years of work experience and does not ask for other languages than English, French, Hindi or Nepali
|
68 |
-
|
69 |
-
Be concise in each answer. Answer in English.
|
70 |
-
|
71 |
-
Example:
|
72 |
-
{{
|
73 |
-
'company_description': 'Galileo Global Education: A leading international network of higher education institutions.',
|
74 |
-
'position_summary': 'Project Manager Marketing and Communication: Develop brand experience, manage marketing/communication plan, ensure brand image, monitor e-reputation, create content, and collaborate with digital team.',
|
75 |
-
'language_requirements': 'French Fluent and English Native',
|
76 |
-
'experience_requirements': 'Previous experience in a similar role, preferably in an agency.',
|
77 |
-
'is_an_internship': false,
|
78 |
-
'salary_range': '€38,000-€42,000',
|
79 |
-
'should_apply': true,
|
80 |
-
}}
|
81 |
-
|
82 |
-
Offer:
|
83 |
-
{}""".format(company, offer)
|
84 |
-
result = call_ai(prompt, True)
|
85 |
-
obj = json.loads(result)
|
86 |
-
print(obj)
|
87 |
-
#Check result
|
88 |
-
if not "company_description" in obj:
|
89 |
-
obj["company_description"] = ""
|
90 |
-
if not "position_summary" in obj:
|
91 |
-
obj["position_summary"] = ""
|
92 |
-
if not "language_requirements" in obj:
|
93 |
-
obj["language_requirements"] = ""
|
94 |
-
if not "experience_requirements" in obj:
|
95 |
-
obj["experience_requirements"] = ""
|
96 |
-
if not "is_an_internship" in obj:
|
97 |
-
obj["is_an_internship"] = False
|
98 |
-
if not "salary_range" in obj:
|
99 |
-
obj["salary_range"] = ""
|
100 |
-
if not "should_apply" in obj:
|
101 |
-
obj["should_apply"] = True
|
102 |
-
|
103 |
-
return obj
|
104 |
|
105 |
def get_job_url(job):
|
106 |
if job["job_url_direct"] == "":
|
|
|
|
|
1 |
import warnings
|
|
|
|
|
|
|
2 |
from jobspy import scrape_jobs
|
3 |
|
4 |
warnings.filterwarnings("ignore")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
+
from ai_manager import get_offer_information
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
def get_job_url(job):
|
9 |
if job["job_url_direct"] == "":
|
jobspy_linkedin.py
CHANGED
@@ -1,107 +1,10 @@
|
|
1 |
-
import json
|
2 |
import warnings
|
3 |
import datetime
|
4 |
-
from mistralai import Mistral, SDKError
|
5 |
-
from time import sleep
|
6 |
|
7 |
from jobspy import scrape_jobs
|
8 |
|
9 |
warnings.filterwarnings("ignore")
|
10 |
-
import
|
11 |
-
|
12 |
-
models = ["mistral-small-2409", "open-mistral-nemo"]
|
13 |
-
|
14 |
-
import random
|
15 |
-
def get_model():
|
16 |
-
return random.choice(models)
|
17 |
-
|
18 |
-
def call_ai(prompt, json_mode):
|
19 |
-
try:
|
20 |
-
return _call_ai(prompt, json_mode)
|
21 |
-
except SDKError as e:
|
22 |
-
#Wait, then try again once
|
23 |
-
sleep(11)
|
24 |
-
return _call_ai(prompt, json_mode)
|
25 |
-
except Exception as e:
|
26 |
-
# Throw the error if it's not an SDKError
|
27 |
-
raise
|
28 |
-
|
29 |
-
def _call_ai(prompt, json_mode):
|
30 |
-
sleep(1.1)
|
31 |
-
client = Mistral(api_key=os.getenv('MISTRAL_KEY'))
|
32 |
-
|
33 |
-
extra_param = {}
|
34 |
-
if json_mode:
|
35 |
-
extra_param = { "response_format" : {"type": "json_object"} }
|
36 |
-
|
37 |
-
chat_response = client.chat.complete(
|
38 |
-
model = get_model(),
|
39 |
-
messages = [
|
40 |
-
{
|
41 |
-
"role": "user",
|
42 |
-
"content": prompt,
|
43 |
-
},
|
44 |
-
],
|
45 |
-
**extra_param
|
46 |
-
)
|
47 |
-
|
48 |
-
return chat_response.choices[0].message.content
|
49 |
-
|
50 |
-
def get_offer_information(company, offer):
|
51 |
-
try:
|
52 |
-
return _get_offer_information(company, offer)
|
53 |
-
except json.decoder.JSONDecodeError as e:
|
54 |
-
#try again once
|
55 |
-
return _get_offer_information(company, offer)
|
56 |
-
except Exception as e:
|
57 |
-
# Throw the error if it's not an SDKError
|
58 |
-
raise
|
59 |
-
|
60 |
-
def _get_offer_information(company, offer):
|
61 |
-
prompt = """This is a job offer from the company '{}', make a JSON with this information:
|
62 |
-
- company_description (string): a description of the company in less than 15 words.
|
63 |
-
- position_summary (string): a summary of the role in 3 bullet points
|
64 |
-
- language_requirements (string): the language requirements in French and English
|
65 |
-
- experience_requirements (string): the experience requirements
|
66 |
-
- is_an_internship (Boolean): true if it's an internship, false otherwise
|
67 |
-
- salary_range (string): the salary range in yearly salary if stated, write 'unknown' otherwise
|
68 |
-
- should_apply (Boolean): True if the offer requires up to 2 years of work experience and does not ask for other languages than English, French, Hindi or Nepali
|
69 |
-
|
70 |
-
Be concise in each answer. Answer in English.
|
71 |
-
|
72 |
-
Example:
|
73 |
-
{{
|
74 |
-
'company_description': 'Galileo Global Education: A leading international network of higher education institutions.',
|
75 |
-
'position_summary': 'Project Manager Marketing and Communication: Develop brand experience, manage marketing/communication plan, ensure brand image, monitor e-reputation, create content, and collaborate with digital team.',
|
76 |
-
'language_requirements': 'French Fluent and English Native',
|
77 |
-
'experience_requirements': 'Previous experience in a similar role, preferably in an agency.',
|
78 |
-
'is_an_internship': false,
|
79 |
-
'salary_range': '€38,000-€42,000',
|
80 |
-
'should_apply': true,
|
81 |
-
}}
|
82 |
-
|
83 |
-
Offer:
|
84 |
-
{}""".format(company, offer)
|
85 |
-
result = call_ai(prompt, True)
|
86 |
-
obj = json.loads(result)
|
87 |
-
print(obj)
|
88 |
-
#Check result
|
89 |
-
if not "company_description" in obj:
|
90 |
-
obj["company_description"] = ""
|
91 |
-
if not "position_summary" in obj:
|
92 |
-
obj["position_summary"] = ""
|
93 |
-
if not "language_requirements" in obj:
|
94 |
-
obj["language_requirements"] = ""
|
95 |
-
if not "experience_requirements" in obj:
|
96 |
-
obj["experience_requirements"] = ""
|
97 |
-
if not "is_an_internship" in obj:
|
98 |
-
obj["is_an_internship"] = False
|
99 |
-
if not "salary_range" in obj:
|
100 |
-
obj["salary_range"] = ""
|
101 |
-
if not "should_apply" in obj:
|
102 |
-
obj["should_apply"] = True
|
103 |
-
|
104 |
-
return obj
|
105 |
|
106 |
def get_job_url(job):
|
107 |
if "{}".format(job["job_url_direct"]) in ["null", "nan", "None"]:
|
|
|
|
|
1 |
import warnings
|
2 |
import datetime
|
|
|
|
|
3 |
|
4 |
from jobspy import scrape_jobs
|
5 |
|
6 |
warnings.filterwarnings("ignore")
|
7 |
+
from ai_manager import get_offer_information
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
def get_job_url(job):
|
10 |
if "{}".format(job["job_url_direct"]) in ["null", "nan", "None"]:
|