├── config ├── companies.txt ├── Resume.pdf └── profile.yaml ├── config.py ├── README.md ├── .gitignore └── workday.py /config/companies.txt: -------------------------------------------------------------------------------- 1 | companysubdomain1 2 | companysubdomain2 3 | companysubdomain3 -------------------------------------------------------------------------------- /config/Resume.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghuboosetty/workday/HEAD/config/Resume.pdf -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import ipdb 3 | 4 | class Config: 5 | def __init__(self, file): 6 | self.file = file 7 | 8 | def read_companies(self): 9 | companies_file = open(self.file, 'r') 10 | company_subdomains = [] 11 | for company_subdomain in companies_file: 12 | company_subdomains.append(company_subdomain.strip()) 13 | companies_file.close() 14 | return company_subdomains 15 | 16 | def write_company(self, company_subdomain): 17 | company_subdomains = self.read_companies() 18 | if company_subdomain in company_subdomains: 19 | return 20 | companies_file = open(self.file, 'a+') 21 | companies_file.writelines("\n"+company_subdomain) 22 | companies_file.close() 23 | 24 | def load_profile(self): 25 | with open(self.file) as profile_file: 26 | profile = yaml.safe_load(profile_file) 27 | return profile -------------------------------------------------------------------------------- /config/profile.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | email: your@email.com 3 | password: Workdaylab@144 4 | first_name: FirstName 5 | family_name: LastName 6 | first_name_local: FirstName 7 | address_line_1: Address1 8 | address_line_2: Address2 9 | address_line_3: Address3 10 | address_city: Hyderabad 11 | address_postal_code: '500089' 12 | phone_number: '9876543210' 13 | linkedin_question: https://www.linkedin.com/in/yourprofile/ 14 | resume_path: "./YourResume.pdf" 15 | education: YourHighestDegreeCollegeName 16 | work_experiences: 17 | - company: LatestCompany 18 | start_year: YYYY 19 | start_month: Aug 20 | end_year: YYYY 21 | end_month: Oct 22 | job_title: YourJobTitle 23 | location: Location 24 | role_description: Your job description in 50-100 words 25 | - company: LatestButOneCompany 26 | start_year: YYYY 27 | start_month: Feb 28 | end_year: YYYY 29 | end_month: Aug 30 | job_title: YourJobTitle 31 | location: Location 32 | role_description: Your job description in 50-100 words 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python script to apply for jobs on Workday 2 | 3 | The Python script to apply for the jobs that are on Workday using the Selenium library and Chrome web driver. 4 | 5 | ## Config 6 | 1. _config/profile.yaml_ - Has all the details related to your profile, you can clone it and change the creds(_email_, _password_, etc..) and other details as per your need 7 | 2. _config/Resume.pdf_ - That's the resume that you need to replace with yours and update _resume_path_ in _config/profile.yaml_ file 8 | 9 | ## Prerequisites 10 | > $ python3 -m pip install --upgrade pip 11 | > 12 | > $ python3 -m pip install --upgrade pip setuptools wheel 13 | > 14 | > $ python3 -m pip install "ipdb" 15 | > 16 | > $ python3 -m pip install "pyyaml" 17 | 18 | ## How to Run the script 19 | > $ python3 workday.py 20 | > 21 | > $ Please share the Workday URL: 22 | > 23 | 24 | #### Example Job URL 25 | [https://gapinc.wd1.myworkdayjobs.com/GAPINC/job/Spoke---Hyderabad/Software-Engineer_R164023/apply?source=JB-10340](https://gapinc.wd1.myworkdayjobs.com/GAPINC/job/Spoke---Hyderabad/Software-Engineer_R164023/apply?source=JB-10340) 26 | 27 | ## Notes 28 | 1. Some workday jobs have few additional mandatory fields in which case the submit may fail but the code rescues it and waits for 60 seconds for the user to manually **fix** the errors and **_Submit_** the form. Please note that if the errors are not fixed manually then the script will eventually fail. 29 | 2. If you run the script again for some reason then it clears all the fields and then adds the information from the _config/profile.yaml_ file. So, please don't assume that it would start from the step where it failed when you re-run the script. 30 | 3. If you want to skip the steps then go to the '_def run_' method in _workday.py_ and comment out the '_fillform_page_1_' or '_fillform_page_2_' or whichever step you want to skip. 31 | 4. For debugging insert this code '**_ipdb.set_trace()_**' at whichever line needed. 32 | 33 | ## Background 34 | There are too many companies that are using Workday as their job board. I have seen that the pattern for all these jobs is 90% similar, and applying on Workday is a real pain in the a**. 35 | It mandates you to log in which requires signup and it never segregates the candidate's profile, your profile is limited to a company and is not carried forward to any other company. Not sure why all MNCs are onboarding to Workday. 36 | 37 | I have thought to put my profile in a JSON file and apply to all the jobs through a Python script. So far, it worked for me like a charm. 38 | 39 | If you too think it benefits you then please feel free to use and suggest any modifications. 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | .DS_Store 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | utils/ 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # poetry 100 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 101 | # This is especially recommended for binary packages to ensure reproducibility, and is more 102 | # commonly ignored for libraries. 103 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 104 | #poetry.lock 105 | 106 | # pdm 107 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 108 | #pdm.lock 109 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 110 | # in version control. 111 | # https://pdm.fming.dev/#use-with-ide 112 | .pdm.toml 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | -------------------------------------------------------------------------------- /workday.py: -------------------------------------------------------------------------------- 1 | import ipdb, time, sys 2 | from selenium import webdriver 3 | from selenium.webdriver.common.by import By 4 | from selenium.webdriver.support.select import Select 5 | from selenium.webdriver.support.ui import WebDriverWait 6 | from selenium.webdriver.support import expected_conditions as EC 7 | from urllib.parse import urlparse 8 | from getpass import getpass 9 | from config import Config 10 | 11 | class Workday: 12 | def __init__(self, url): 13 | self.url = url 14 | self.profile = Config('utils/profile.yaml').load_profile() 15 | self.companies_file = Config('utils/companies.txt') 16 | 17 | # Set up Selenium WebDriver 18 | self.driver = webdriver.Chrome() # You need to have chromedriver installed 19 | self.wait = WebDriverWait(self.driver, 10) 20 | self.driver.maximize_window() 21 | 22 | def signup(self): 23 | button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-automation-id='createAccountLink']"))) 24 | button.click() 25 | time.sleep(10) 26 | 27 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='email']").send_keys(self.profile['email']) 28 | self.driver.find_element(By.CSS_SELECTOR, "input[type='password'][data-automation-id='password']").send_keys(self.profile['password']) 29 | self.driver.find_element(By.CSS_SELECTOR, "input[type='password'][data-automation-id='verifyPassword']").send_keys(self.profile['password']) 30 | try: 31 | self.driver.find_element(By.CSS_SELECTOR, "input[type='checkbox'][data-automation-id='createAccountCheckbox']").click() 32 | except: 33 | print("Exception: 'There is no checkbox for signup'") 34 | button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[role='button'][aria-label='Create Account'][data-automation-id='click_filter']"))) 35 | time.sleep(1) 36 | button.click() 37 | time.sleep(2) 38 | 39 | def signin(self): 40 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='email']").send_keys(self.profile['email']) 41 | self.driver.find_element(By.CSS_SELECTOR, "input[type='password'][data-automation-id='password']").send_keys(self.profile['password']) 42 | button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[role='button'][aria-label='Sign In'][data-automation-id='click_filter']"))) 43 | time.sleep(1) 44 | button.click() 45 | 46 | def fillform_page_1(self): 47 | try: 48 | # put radio button at the beginning as it sometimes goes to unclickable state 49 | self.driver.switch_to.active_element 50 | self.driver.find_element(By.CSS_SELECTOR, "input[type='radio'][data-uxi-element-id='radio_2']").click() 51 | except: 52 | print("Exception: 'No radio_2 button'") 53 | 54 | try: 55 | self.driver.find_element(By.CSS_SELECTOR, "div[data-automation-id='multiSelectContainer']").click() 56 | time.sleep(3) 57 | self.driver.find_element(By.CSS_SELECTOR, "div[data-automation-id='promptOption'][data-automation-label='Job Board']").click() 58 | time.sleep(3) 59 | self.driver.find_element(By.CSS_SELECTOR, "div[data-automation-id='promptOption'][data-automation-label='LinkedIn']").click() 60 | time.sleep(5) 61 | except: 62 | print("Exception: 'No Source selector'") 63 | 64 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='legalNameSection_firstName']").clear() 65 | time.sleep(2) 66 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='legalNameSection_firstName']").send_keys(self.profile['first_name']) 67 | 68 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='legalNameSection_lastName']").clear() 69 | time.sleep(2) 70 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='legalNameSection_lastName']").send_keys(self.profile['family_name']) 71 | 72 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='legalNameSection_firstNameLocal']").clear() 73 | time.sleep(2) 74 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='legalNameSection_firstNameLocal']").send_keys(self.profile['first_name_local']) 75 | 76 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_addressLine1']").clear() 77 | time.sleep(2) 78 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_addressLine1']").send_keys(self.profile['address_line_1']) 79 | 80 | try: 81 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_addressLine2']").clear() 82 | time.sleep(2) 83 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_addressLine2']").send_keys(self.profile['address_line_2']) 84 | except: 85 | print("Exception: 'No addressSection_addressLine2'") 86 | 87 | try: 88 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_addressLine3']").clear() 89 | time.sleep(2) 90 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_addressLine3']").send_keys(self.profile['address_line_3']) 91 | except: 92 | print("Exception: 'No addressSection_addressLine3'") 93 | 94 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_city']").clear() 95 | time.sleep(2) 96 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_city']").send_keys(self.profile['address_city']) 97 | 98 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_postalCode']").clear() 99 | time.sleep(2) 100 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_postalCode']").send_keys(self.profile['address_postal_code']) 101 | 102 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='addressSection_postalCode']").location_once_scrolled_into_view 103 | 104 | try: 105 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][data-automation-id='addressSection_countryRegion']").click() 106 | time.sleep(5) 107 | self.driver.find_element(By.XPATH, "//div[text()='Telangana']").click() 108 | time.sleep(2) 109 | except: 110 | print("Exception: 'No addressSection_countryRegion'") 111 | 112 | try: 113 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][data-automation-id='phone-device-type']").click() 114 | time.sleep(5) 115 | self.driver.find_element(By.XPATH, "//div[text()='Mobile']").click() 116 | time.sleep(2) 117 | except: 118 | print("Exception: 'Mobile not present'") 119 | 120 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='phone-number']").clear() 121 | time.sleep(2) 122 | self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='phone-number']").send_keys(self.profile['phone_number']) 123 | time.sleep(2) 124 | 125 | def fillform_page_2(self): 126 | delete_work_experiences = self.driver.find_elements(By.CSS_SELECTOR, "button[data-automation-id='panel-set-delete-button']") 127 | i = 1 128 | while i <= len(delete_work_experiences): 129 | self.driver.find_element(By.CSS_SELECTOR, "button[data-automation-id='panel-set-delete-button']").click() 130 | i = i+1 131 | time.sleep(1) 132 | 133 | for work_experience_index, work_experience in enumerate(self.profile['work_experiences']): 134 | if work_experience_index == 0: 135 | try: 136 | self.driver.find_element(By.CSS_SELECTOR, "button[data-automation-id='Add']").click() 137 | except: 138 | print("Exception: 'Add button not found'") 139 | else: 140 | self.driver.find_element(By.CSS_SELECTOR, "button[data-automation-id='Add Another']").click() 141 | 142 | time.sleep(2) 143 | work_experience_div = self.driver.find_element(By.CSS_SELECTOR, "div[data-automation-id='workExperience-"+str(work_experience_index+1)+"']") 144 | work_experience.update({'div': work_experience_div}) 145 | self.fill_work_experience(work_experience) 146 | time.sleep(2) 147 | 148 | resume_section = self.driver.find_element(By.CSS_SELECTOR, "div[data-automation-id='resumeSection']") 149 | resume_section.location_once_scrolled_into_view 150 | time.sleep(2) 151 | 152 | delete_resumes = self.driver.find_elements(By.CSS_SELECTOR, "button[data-automation-id='delete-file']") 153 | i = 1 154 | while i <= len(delete_resumes): 155 | self.driver.find_element(By.CSS_SELECTOR, "button[data-automation-id='delete-file']").click() 156 | i = i+1 157 | time.sleep(1) 158 | 159 | file_input = self.driver.find_element(By.CSS_SELECTOR, "input[type='file']") 160 | file_input.send_keys(self.profile['resume_path']) 161 | time.sleep(10) 162 | 163 | try: 164 | linkedin_question = self.driver.find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='linkedinQuestion']") 165 | linkedin_question.clear() 166 | linkedin_question.send_keys(self.profile['linkedin_question']) 167 | except: 168 | print("Exception: 'No Linkedin input'") 169 | 170 | def fillform_page_3(self): 171 | try: 172 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][aria-label='Are you legally authorized to work in the country to which you are applying? select one required']").click() 173 | time.sleep(2) 174 | self.driver.find_element(By.XPATH, "//div[text()='Yes']").click() 175 | time.sleep(2) 176 | except: 177 | print("Exception: 'Work Authorization already selected'") 178 | 179 | try: 180 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][aria-label='Will you require sponsorship to continue and/or extend your current work authorization status? select one required']").click() 181 | time.sleep(2) 182 | self.driver.find_element(By.XPATH, "//div[text()='No']").click() 183 | time.sleep(2) 184 | except: 185 | print("Exception: 'Visa Sponsorship already selected'") 186 | 187 | def fillform_page_4(self): 188 | try: 189 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][data-automation-id='gender']").click() 190 | time.sleep(2) 191 | self.driver.find_element(By.XPATH, "//div[text()='Male']").click() 192 | time.sleep(2) 193 | except: 194 | print("Exception: 'Gender not present'") 195 | 196 | try: 197 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][data-automation-id='nationality']").click() 198 | time.sleep(2) 199 | self.driver.find_element(By.XPATH, "//div[text()='India']").click() 200 | time.sleep(2) 201 | except: 202 | print("Exception: 'Nationality not present") 203 | 204 | try: 205 | agreement_checkbox = self.driver.find_element(By.CSS_SELECTOR, "input[type='checkbox'][data-automation-id='agreementCheckbox']") 206 | agreement_checkbox.location_once_scrolled_into_view 207 | agreement_checkbox.click() 208 | except: 209 | print("Exception: agreementCheckbox not present") 210 | 211 | def fill_work_experience(self, work_experience): 212 | work_experience['div'].find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='jobTitle']").send_keys(work_experience['job_title']) 213 | work_experience['div'].find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='company']").send_keys(work_experience['company']) 214 | try: 215 | location = work_experience['div'].find_element(By.CSS_SELECTOR, "input[type='text'][data-automation-id='location']") 216 | location.send_keys(work_experience['location']) 217 | except: 218 | print("Exception: 'no location field in experience'") 219 | work_experience['div'].find_element(By.CSS_SELECTOR, "textarea[data-automation-id='description']").send_keys(work_experience['role_description']) 220 | 221 | # location.location_once_scrolled_into_view 222 | 223 | start_date_div = work_experience['div'].find_element(By.CSS_SELECTOR, "div[data-automation-id='formField-startDate']") 224 | start_date_div.find_element(By.CSS_SELECTOR, "div[role='button'][data-automation-id='dateIcon']").click() 225 | time.sleep(2) 226 | month_picker = self.driver.find_element(By.CSS_SELECTOR, "span[data-automation-id='monthPickerSpinnerLabel']") 227 | while month_picker.text != work_experience['start_year']: 228 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][data-automation-id='monthPickerLeftSpinner']").click() 229 | self.driver.find_element(By.XPATH, "//label[text()='"+work_experience['start_month']+"']").click() 230 | 231 | end_date_div = work_experience['div'].find_element(By.CSS_SELECTOR, "div[data-automation-id='formField-endDate']") 232 | end_date_div.find_element(By.CSS_SELECTOR, "div[role='button'][data-automation-id='dateIcon']").click() 233 | time.sleep(2) 234 | month_picker = self.driver.find_element(By.CSS_SELECTOR, "span[data-automation-id='monthPickerSpinnerLabel']") 235 | while month_picker.text != work_experience['end_year']: 236 | self.driver.find_element(By.CSS_SELECTOR, "button[type='button'][data-automation-id='monthPickerLeftSpinner']").click() 237 | self.driver.find_element(By.XPATH, "//label[text()='"+work_experience['end_month']+"']").click() 238 | 239 | def click_next(self): 240 | button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-automation-id='bottom-navigation-next-button']"))) 241 | button.click() 242 | try: 243 | error_button = self.driver.find_element(By.CSS_SELECTOR, "button[data-automation-id='errorBanner']") 244 | print("Exception: 'Errors on page. Please resolve and submit manually. You have 60 seconds to do so!'") 245 | time.sleep(60) 246 | except: 247 | print("No Errors") 248 | time.sleep(10) 249 | 250 | def run(self): 251 | parsed_url = urlparse(self.url) 252 | company = parsed_url.netloc.split('.')[0] 253 | existing_company = company in self.companies_file.read_companies() 254 | 255 | self.driver.get(self.url) # Open a webpage 256 | time.sleep(5) 257 | 258 | # accept cookies 259 | try: 260 | button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-automation-id='legalNoticeAcceptButton']"))) 261 | button.click() 262 | except: 263 | print("Exception: 'No button for Cookies!") 264 | 265 | try: 266 | if existing_company: 267 | self.signin() 268 | else: 269 | self.signup() 270 | self.companies_file.write_company(company) 271 | except: 272 | print("No Signup/Signin - Apply without Signin") 273 | 274 | time.sleep(5) 275 | if len(self.driver.find_elements(By.CSS_SELECTOR, "div[data-automation-id='alreadyApplied']")) > 0: 276 | print("alreadyApplied job, exiting the program...") 277 | self.driver.quit() 278 | sys.exit() 279 | 280 | try: 281 | button = self.driver.find_element(By.CSS_SELECTOR, "a[role='button'][data-automation-id='applyManually']") 282 | button.click() 283 | except: 284 | print("NoSuchElementException") 285 | 286 | time.sleep(10) 287 | self.fillform_page_1() 288 | self.click_next() 289 | 290 | self.fillform_page_2() 291 | self.click_next() 292 | 293 | self.fillform_page_3() 294 | self.click_next() 295 | 296 | self.fillform_page_4() 297 | self.click_next() 298 | 299 | # review and submit 300 | self.click_next() 301 | 302 | # Wait for half minute 303 | time.sleep(30) 304 | 305 | # Close the browser 306 | self.driver.quit() 307 | 308 | print("Please share Workday URL:") 309 | url = str(input()) 310 | 311 | workday = Workday(url) 312 | # ipdb.set_trace() 313 | workday.run() --------------------------------------------------------------------------------