Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PDF Resume Upload, Fill out education information, Incognito Mode #48

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions constants/common.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,65 @@
YES = 'Yes'
NO = 'No'
GENDERS_LIST = ['Male', 'Female', 'Other']



REFERRAL_LIST = [
'Career Event',
'Company Website',
'Employee Referral',
'Job Board',
'Print Advertisement',
'Recruiter ',
'Search Engine (Google, Bing, etc.)',
'Social Media',
'University Recruiting'
]

ETHNICITY_LIST = [
'American Indian/Alaskan Native',
'Asian',
'Black or African American',
'Hispanic or Latino',
'Native Hawaiian or Other Pacific Island',
'Two or More Races',
'Undeclared',
'White'
]

DISABILITY_LIST = [
'No disability',
'Not willing to answer',
'Yes, with a disability (or previously had a disability)'
]

# Incomplete List
INDUSTRY_LIST = [
'Construction/Mining',
'Custom Services',
'Distribution Service',
'Finance Services',
'Food Industry',
'Goods Transportation',
'Government',
'Health Care',
'Human Resources',
'Information Technology Services'
]

# Incomplete List
GRADUATION_STATUS = [
'Graduated - Degree Received',
'Completed - Non Degree Program'
]

# Incomplete List
DEGREE_MAJORS = [
'Baking Science & Technology',
'Biology'
]

# Incomplete List
DEGREE_TYPES = [
'Bachelors Degree (Bachelor of Science or Arts)'
]
32 changes: 23 additions & 9 deletions constants/elementIds.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
COUNTRY_REGION_CODE_LABEL = 'fbclc_ituCode'
COUNTRY_REGION_OF_RESIDENCE_LABEL = 'fbclc_country'
CITIZEN_QUESTION_LABEL = '154:_select'
COUNTRY_OF_ORIGIN_LABEL = '195:_select'
EIGHTEEN_YEARS_OLD_LABEL = '211:_select'
REQUIRE_SPONSORSHIP_LABEL = '215:_select'
PREVIOUSLY_WORKED_LABEL = '219:_select'
PREVIOUSLY_PARTNERED_LABEL = '223:_select'
RELATIVE_WORKER_LABEL = '227:_select'
ESSENTIAL_FUNCTIONS_LABEL = '231:_select'
GENDER_LABEL = '235:_select'

# Candidate-Specific Information
CITIZEN_QUESTION_LABEL = 'citizen'
COUNTRY_OF_ORIGIN_LABEL = 'custCountry'
EIGHTEEN_YEARS_OLD_LABEL = 'custAge'
REQUIRE_SPONSORSHIP_LABEL = 'cust_sponsor'
PREVIOUSLY_WORKED_LABEL = 'custPrev'
PREVIOUSLY_PARTNERED_LABEL = 'custcontr'
RELATIVE_WORKER_LABEL = 'custRel'
ESSENTIAL_FUNCTIONS_LABEL = 'custAccom'
GENDER_LABEL = 'custgender'
REFERRAL_LABEL = 'candidateSource'
ETHNICITY_LABEL = 'ethnicity'
DISABILITY_LABEL = 'disabilityselection'

# Captcha
RECAPTCHA_AUDIO_BUTTON = 'recaptcha-audio-button'
RECAPTCHA_ANCHOR = 'recaptcha-anchor'
AUDIO_SOURCE = 'audio-source'
AUDIO_RESPONSE = 'audio-response'

# Profile Information
STATE_LABEL = 'state'
PRESENT_EMPLOYEE = 'presentEmployer'

# Outside Work Experience
INDUSTRY_LABEL = 'VFLD2'
19 changes: 12 additions & 7 deletions constants/xPaths.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
XPATHS_1 = {
'resume': '//*[@id="49:_file"]',
'addy': '//*[@id="69:_txtFld"]',
'city': '//*[@id="73:_txtFld"]',
'zip': '//*[@id="81:_txtFld"]',
'job': '//*[@id="101:_txtFld"]',
'salary': '//*[@id="172:_txtFld"]',
'country': '//*[@id="195:_select"]'
'addy': '//*[@name="address"]',
'city': '//*[@name="city"]',
'zip': '//*[@name="zip"]',
'job': '//*[@name="currentTitle"]',
'salary': '//*[@name="expectedSalaryRange"]',
'country': '//*[@name="country"]'
}

XPATHS_2 = {
Expand All @@ -30,4 +30,9 @@
MIXER_QUESTION_1_LABEL = '//label[text()="350 LBS"]'
MIXER_QUESTION_2_LABEL = '//label[text()="800 LBS"]'
LONG_PERIODS_QUESTION_LABEL = '//label[text()="Yes"]'
APPLY_BUTTON = '//*[@id="261:_submitBtn"]'
APPLY_BUTTON = '//span[text()="Apply"]'

# Education Info (using 'select' instead of * for dropdown items)
DEGREE_COMPLETION_LABEL = '//select[@name="VFLD4"]'
DEGREE_MAJOR_LABEL = '//select[@name="VFLD2"]'
DEGREE_TYPE_LABEL = '//select[@name="VFLD3"]'
76 changes: 56 additions & 20 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.keys import Keys
from resume_faker import make_resume
from pdf2image import convert_from_path

from webdriver_manager.chrome import ChromeDriverManager
os.environ['WDM_LOG_LEVEL'] = '0'
Expand Down Expand Up @@ -115,7 +114,10 @@ def solveCaptcha(driver):
driver.switch_to.default_content()

def start_driver(random_city):
driver = webdriver.Chrome(ChromeDriverManager().install())
options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-logging'])
options.add_argument('--incognito')
driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)
driver.get(CITIES_TO_URLS[random_city])
driver.implicitly_wait(10)
time.sleep(2)
Expand Down Expand Up @@ -168,8 +170,6 @@ def fill_out_application_and_submit(driver, random_city, fake_identity):
# make resume
resume_filename = fake_identity['last_name']+'-Resume'
make_resume(fake_identity['first_name']+' '+fake_identity['last_name'], fake_identity['email'], resume_filename+'.pdf')
images = convert_from_path(resume_filename+'.pdf')
images[0].save(resume_filename+'.png', 'PNG')

# fill out form parts of app
driver.find_element_by_xpath(PROFILE_INFORMATION_DROPDOWN).click()
Expand All @@ -180,58 +180,74 @@ def fill_out_application_and_submit(driver, random_city, fake_identity):
match key:
case 'resume':
driver.find_element_by_xpath(UPLOAD_A_RESUME_BUTTON).click()
info = os.getcwd() + '/'+resume_filename+'.png'
info = os.getcwd() + '/'+resume_filename+'.pdf'
case 'addy':
info = fake.street_address()
case 'city':
info = random_city
case 'zip':
info = CITIES_TO_ZIP_CODES[random_city]
info = CITIES_TO_ZIP_CODES[random_city][random.randint(0,5)]
case 'job':
info = fake.job()
case 'salary':
info = random.randint(15, 35)
first = random.randrange(15, 30, 5)
info = f'{first}-{random.randrange(first + 5, 35, 5)}'

driver.find_element_by_xpath(XPATHS_1.get(key)).send_keys(info)
if key == 'resume': time.sleep(8) # wait for "loading" animation

print(f"successfully filled out app forms for {random_city}")

# fill out dropdowns
select = Select(driver.find_element_by_id(CITIZEN_QUESTION_LABEL))
select = Select(driver.find_element_by_name(CITIZEN_QUESTION_LABEL))
select.select_by_visible_text(YES)
select = Select(driver.find_element_by_id(COUNTRY_OF_ORIGIN_LABEL))
select = Select(driver.find_element_by_name(COUNTRY_OF_ORIGIN_LABEL))
select.select_by_visible_text(FULL_NAME_US)
select = Select(driver.find_element_by_id(EIGHTEEN_YEARS_OLD_LABEL))
select = Select(driver.find_element_by_name(EIGHTEEN_YEARS_OLD_LABEL))
select.select_by_visible_text(YES)
select = Select(driver.find_element_by_id(REQUIRE_SPONSORSHIP_LABEL))
select = Select(driver.find_element_by_name(REQUIRE_SPONSORSHIP_LABEL))
select.select_by_visible_text(NO)
select = Select(driver.find_element_by_id(PREVIOUSLY_WORKED_LABEL))
select = Select(driver.find_element_by_name(PREVIOUSLY_WORKED_LABEL))
select.select_by_visible_text(NO)
select = Select(driver.find_element_by_id(PREVIOUSLY_PARTNERED_LABEL))
select = Select(driver.find_element_by_name(PREVIOUSLY_PARTNERED_LABEL))
select.select_by_visible_text(NO)
select = Select(driver.find_element_by_id(RELATIVE_WORKER_LABEL))
select = Select(driver.find_element_by_name(RELATIVE_WORKER_LABEL))
select.select_by_visible_text(NO)
select = Select(driver.find_element_by_id(ESSENTIAL_FUNCTIONS_LABEL))
select = Select(driver.find_element_by_name(ESSENTIAL_FUNCTIONS_LABEL))
select.select_by_visible_text(YES)
select = Select(driver.find_element_by_id(PREVIOUSLY_PARTNERED_LABEL))
select = Select(driver.find_element_by_name(PREVIOUSLY_PARTNERED_LABEL))
select.select_by_visible_text(NO)
time.sleep(1)
select = Select(driver.find_element_by_id(GENDER_LABEL))
select = Select(driver.find_element_by_name(GENDER_LABEL))
gender = random.choice(GENDERS_LIST)
select.select_by_visible_text(gender)
driver.find_element_by_xpath(MIXER_QUESTION_1_LABEL).click()
driver.find_element_by_xpath(MIXER_QUESTION_2_LABEL).click()
driver.find_element_by_xpath('//select[@name="' + STATE_LABEL + '"]/option[text()="' + CITIES_TO_STATES[random_city] + '"]').click()
driver.find_element_by_xpath('//select[@name="' + PRESENT_EMPLOYEE + '"]/option[text()="' + random.choice([YES, NO]) + '"]').click()
driver.find_element_by_xpath('//select[@name="' + REFERRAL_LABEL + '"]/option[text()="' + random.choice(REFERRAL_LIST) + '"]').click()
driver.find_element_by_xpath('//select[@name="' + ETHNICITY_LABEL + '"]/option[text()="' + random.choice(ETHNICITY_LIST) + '"]').click()

els = driver.find_elements_by_xpath(LONG_PERIODS_QUESTION_LABEL)
[el.click() for el in els]

time.sleep(5)
fill_out_education_info(driver)
fill_out_work_history(driver)

time.sleep(3)
driver.find_element_by_xpath(APPLY_BUTTON).click()
print(f"successfully submitted application")
time.sleep(3)
try:
driver.find_element_by_xpath('//*[@class="rcmSuccessBackToResultsBtn"]')
print(f"successfully submitted application")
except Exception as e:
print(e)
print('There may be unfilled items. Stop script and complete the application manually or wait to abort')
time.sleep(5)

# take out the trash
os.remove(resume_filename+'.pdf')
os.remove(resume_filename+'.png')


def random_email(name=None):
if name is None:
Expand All @@ -254,6 +270,26 @@ def random_email(name=None):
random.choices(EMAIL_DATA, emailChoices)[0][1]


def fill_out_education_info(driver):
try:
driver.find_element_by_xpath(DEGREE_COMPLETION_LABEL + '/option[text()="' + random.choice(GRADUATION_STATUS) + '"]').click()
driver.find_element_by_xpath(DEGREE_MAJOR_LABEL + '/option[text()="' + random.choice(DEGREE_MAJORS) + '"]').click()
driver.find_element_by_xpath(DEGREE_TYPE_LABEL + '/option[text()="' + random.choice(DEGREE_TYPES) + '"]').click()
print('successfully filled out degree information')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance you could merge in #51 and have the resume & website agree on university and degree? Otherwise, this looks fantastic.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged in the latex_resumes. The university is auto-filled from the resume, so luckily we don't have to touch that. I still need to copy the long list of degree majors from the site's dropdown list.

Because the "major/area of study" field is a dropdown, I need specific strings in order to match the selection (I can't type my own option). To make this easier, once I copy over the list of majors, could you modify the resume generator to only use those major names?

The list of majors will be in "common.py" under the variable name "DEGREE_MAJORS"

Also I can't be sure if its still working after the merge, cause email verification. Hopefully there'll be a fix in a few hours when I have time to work on it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely, should be an easy fix. It’ll likely auto-populate once that’s done, won’t it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe it's capable of auto-populating dropdown menus. I'm writing a summary of the auto-fill behavior I've noticed and I'll comment it on your PR

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if make_resume() returned the university and degree it chose, so that main.py can use those same values to “manually” fill those dropdowns? or, alternatively (or simultaneously), make_resume() accepted university and job as optional parameters, so that main.py could generate them and pass them along just like it does with name, email, and phone?

I can experiment with both of those with my own commits, if you prefer.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could store degree names in a dict, like:

DEGREES = [
  { 'dropdown': 'Bachelors Degree (Bachelor of Science or Arts) Aviation', 'resume': 'Bachelor of Science in Aviation' },
  …
]

(or even define a small degree object, if we want to be fancy). That way we wouldn’t have to worry about parsing from one to the other.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of storing everything in fake_identity for sure.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok as I'm looking through resume_faker.py , I think we're overcomplicating things. Everything currently in that file is a Bachelor's Degree of some sort, so they all correspond to "Bachelors Degree (Bachelor of Science or Arts)". Simple as that

We could add in a bunch of associates degrees as a separate list if we want to mix up the results, then they would all correspond to "Associates Degree/Diploma" in the form. I think these degrees are most in line with what Kelloggs would actually be looking for, and they're extremely common degrees, so we could just keep it at that.

Thinking about adding "High School Diploma" as well, but I'm realizing that if there isn't a college listed on the resume, the form won't autofill the college. Maybe we can experiment with putting high school names in the resume and seeing if the form still auto-fills the education section

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like you’re right about us overcomplicating it. We could add associate’s degrees, but i’m not so sure we should be worrying about a recruiter being able to manually tell the difference between a fake resume and a real one (the bullet points are all gibberish anyway), so i don’t think the specific degree matters much (in my opinion).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah let's just keep it to Bachelors Degrees only (which is how it already is) and a random major. So I just need to add in the rest of the majors and this will all be dealt with

except Exception as e:
print(f'Education info probably not required: {e}')
time.sleep(2)


def fill_out_work_history(driver):
for i in range(2):
try:
driver.find_element_by_xpath('(//select[@name="' + INDUSTRY_LABEL + '"]/option[text()="' + random.choice(INDUSTRY_LIST) + '"])[' + str(i+1) + ']').click()
except Exception as e:
print(f'Probably no work experience section: {e}')
pass


def main():
while True:
random_city = random.choice(list(CITIES_TO_URLS.keys()))
Expand Down
4 changes: 2 additions & 2 deletions resume_faker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1823,12 +1823,12 @@ def make_resume(name, email, path = 'output.pdf'):
'Howard College',
'Howard Payne University',
'Lamar University',
'Lamar State College Port Arthur',
'Lamar State College - Port Arthur',
'Lubbock Christian University',
'LeTourneau University',
'Lamar Institute of Technology',
'Lone Star College System',
'Lamar State College Orange',
'Lamar State College - Orange',
'McLennan Community College',
'McMurry University',
'Midland College',
Expand Down