From 8d006f2d073b76fe985a8f0a6ae63146f0436daa Mon Sep 17 00:00:00 2001 From: krapes Date: Thu, 16 Jul 2020 16:13:53 -0400 Subject: [PATCH 1/7] Feature: Better support for cases where a line for output file exists in config.yaml but does not contain anything --- easyapplybot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/easyapplybot.py b/easyapplybot.py index d1a35ac..f1a00d0 100644 --- a/easyapplybot.py +++ b/easyapplybot.py @@ -383,7 +383,8 @@ def finish_apply(self): print(parameters) cover_letter_loctn = parameters.get('cover_letter_loctn', [None])[0] - output_filename = parameters.get('output_filename', ['output.csv'])[0] + output_filename = [f for f in parameters.get('output_filename', ['output.csv']) if f != None] + output_filename = output_filename[0] if len(output_filename) > 0 else 'output.csv' blacklist = parameters.get('blacklist', []) bot = EasyApplyBot(parameters['username'], From d93501f93c03cc33badb3df4bf9f3c2ebba5b217 Mon Sep 17 00:00:00 2001 From: krapes Date: Fri, 17 Jul 2020 15:17:07 -0400 Subject: [PATCH 2/7] Feature: First draft of photo upload working --- easyapplybot.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/easyapplybot.py b/easyapplybot.py index f1a00d0..417006a 100644 --- a/easyapplybot.py +++ b/easyapplybot.py @@ -25,7 +25,7 @@ class EasyApplyBot: MAX_SEARCH_TIME = 10*60 - + photo = '/home/kerri/Pictures/surfing.jpg' def __init__(self, username, @@ -164,7 +164,7 @@ def applications_loop(self, position, location): jobIDs = [x for x in IDs if x not in self.appliedJobIDs] after = len(jobIDs) - + jobIDs = [1943232413] if len(jobIDs) == 0 and len(IDs) > 24: jobs_per_page = jobs_per_page + 25 count_job = 0 @@ -275,17 +275,31 @@ def is_present(button_locator): "button[aria-label='Submit application']") error_locator = (By.CSS_SELECTOR, "p[data-test-form-element-error-message='true']") - cover_letter = (By.CSS_SELECTOR, "input[name='file']") + upload_locator = (By.CSS_SELECTOR, "input[name='file']") + + submitted = False while True: # Upload Cover Letter if possible - if is_present(cover_letter): - input_button = self.browser.find_elements(cover_letter[0], - cover_letter[1]) - - input_button[0].send_keys(self.cover_letter_loctn) + if is_present(upload_locator): + + input_buttons = self.browser.find_elements(upload_locator[0], + upload_locator[1]) + for input_button in input_buttons: + parent = input_button.find_element(By.XPATH, "..") + sibling = parent.find_element(By.XPATH, "preceding-sibling::*") + print(sibling) + print(sibling.text) + if 'Photo' in sibling.text: + input_button.send_keys(self.photo) + + grandparent = sibling.find_element(By.XPATH, "..") + print(grandparent) + print(grandparent.text) + + #input_button[0].send_keys(self.cover_letter_loctn) time.sleep(random.uniform(4.5, 6.5)) # Click Next or submitt button if possible From 288d9a6960f1d6917bae8b168539be8c66f98261 Mon Sep 17 00:00:00 2001 From: krapes Date: Fri, 17 Jul 2020 17:11:53 -0400 Subject: [PATCH 3/7] Feature: uploads dict working --- easyapplybot.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/easyapplybot.py b/easyapplybot.py index 417006a..0dd2ced 100644 --- a/easyapplybot.py +++ b/easyapplybot.py @@ -25,12 +25,12 @@ class EasyApplyBot: MAX_SEARCH_TIME = 10*60 - photo = '/home/kerri/Pictures/surfing.jpg' + #photo = '/home/kerri/Pictures/surfing.jpg' def __init__(self, username, password, - cover_letter_loctn=None, + uploads={}, filename='output.csv', blacklist=[]): @@ -38,7 +38,8 @@ def __init__(self, dirpath = os.getcwd() print("current directory is : " + dirpath) - self.cover_letter_loctn = cover_letter_loctn + #self.cover_letter_loctn = cover_letter_loctn + self.uploads = uploads self.appliedJobIDs = self.get_appliedIDs(filename) if self.get_appliedIDs(filename) != None else [] self.filename = filename self.options = self.browser_options() @@ -96,7 +97,6 @@ def fill_data(self): self.browser.set_window_size(0, 0) self.browser.set_window_position(2000, 2000) - print(self.cover_letter_loctn) def start_apply(self, positions, locations): start = time.time() @@ -164,7 +164,6 @@ def applications_loop(self, position, location): jobIDs = [x for x in IDs if x not in self.appliedJobIDs] after = len(jobIDs) - jobIDs = [1943232413] if len(jobIDs) == 0 and len(IDs) > 24: jobs_per_page = jobs_per_page + 25 count_job = 0 @@ -290,14 +289,11 @@ def is_present(button_locator): for input_button in input_buttons: parent = input_button.find_element(By.XPATH, "..") sibling = parent.find_element(By.XPATH, "preceding-sibling::*") - print(sibling) - print(sibling.text) - if 'Photo' in sibling.text: - input_button.send_keys(self.photo) - grandparent = sibling.find_element(By.XPATH, "..") - print(grandparent) - print(grandparent.text) + for key in self.uploads.keys(): + if key in sibling.text or key in grandparent.text: + input_button.send_keys(self.uploads[key]) + #input_button[0].send_keys(self.cover_letter_loctn) time.sleep(random.uniform(4.5, 6.5)) @@ -381,6 +377,9 @@ def next_jobs_page(self, position, location, jobs_per_page): def finish_apply(self): self.browser.close() + + + if __name__ == '__main__': with open("config.yaml", 'r') as stream: @@ -396,14 +395,17 @@ def finish_apply(self): print(parameters) - cover_letter_loctn = parameters.get('cover_letter_loctn', [None])[0] + #cover_letter_loctn = parameters.get('cover_letter_loctn', [None])[0] output_filename = [f for f in parameters.get('output_filename', ['output.csv']) if f != None] output_filename = output_filename[0] if len(output_filename) > 0 else 'output.csv' blacklist = parameters.get('blacklist', []) + uploads = parameters.get('uploads', {}) + for key in uploads.keys(): + assert uploads[key] != None bot = EasyApplyBot(parameters['username'], parameters['password'], - cover_letter_loctn=cover_letter_loctn, + uploads=uploads, filename=output_filename, blacklist=blacklist ) From 9506c7d7226becad7023dd4f50bcdb167e1abb31 Mon Sep 17 00:00:00 2001 From: krapes Date: Fri, 17 Jul 2020 17:12:58 -0400 Subject: [PATCH 4/7] Feature: update config --- config.yaml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/config.yaml b/config.yaml index 655292f..af5b02b 100644 --- a/config.yaml +++ b/config.yaml @@ -2,19 +2,23 @@ username: password: positions: -- # positions you want to search for +- Data Scientist - # Another position you want to search for - # A third position you want to search for locations: -- # Location you want to search for +- Remote - # A second location you want to search in -cover_letter_loctn: -- # '/home/PATH_TO_FILE' +# --------- Optional Parameters ------- +# uploads: +# Resume: # PATH TO Resume +# Cover Letter: # PATH TO cover letter +# Photo: # PATH TO photo -output_filename: -- # PATH TO OUTPUT FILE (default output.csv) -blacklist: -- # Company names you want to ignore \ No newline at end of file +# output_filename: +# - # PATH TO OUTPUT FILE (default output.csv) + +# blacklist: +# - # Company names you want to ignore \ No newline at end of file From 076a08d95f7906b56375dc491f1c2aba4e1f11ff Mon Sep 17 00:00:00 2001 From: krapes Date: Fri, 17 Jul 2020 17:37:08 -0400 Subject: [PATCH 5/7] Refactor --- easyapplybot.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/easyapplybot.py b/easyapplybot.py index 0dd2ced..7c90ee8 100644 --- a/easyapplybot.py +++ b/easyapplybot.py @@ -25,7 +25,6 @@ class EasyApplyBot: MAX_SEARCH_TIME = 10*60 - #photo = '/home/kerri/Pictures/surfing.jpg' def __init__(self, username, @@ -38,9 +37,9 @@ def __init__(self, dirpath = os.getcwd() print("current directory is : " + dirpath) - #self.cover_letter_loctn = cover_letter_loctn self.uploads = uploads - self.appliedJobIDs = self.get_appliedIDs(filename) if self.get_appliedIDs(filename) != None else [] + past_ids = self.get_appliedIDs(filename) + self.appliedJobIDs = past_ids if past_ids != None else [] self.filename = filename self.options = self.browser_options() self.browser = driver @@ -50,13 +49,12 @@ def __init__(self, def get_appliedIDs(self, filename): - print(filename) try: df = pd.read_csv(filename, header=None, names=['timestamp', 'jobID', 'job', 'company', 'attempted', 'result'], lineterminator='\n', - encoding = 'utf-8') + encoding='utf-8') df['timestamp'] = pd.to_datetime(df['timestamp'], format="%Y-%m-%d %H:%M:%S.%f") df = df[df['timestamp'] > (datetime.now() - timedelta(days=2))] @@ -92,12 +90,10 @@ def start_linkedin(self,username,password): except TimeoutException: print("TimeoutException! Username/password field or login button not found") - def fill_data(self): self.browser.set_window_size(0, 0) self.browser.set_window_position(2000, 2000) - def start_apply(self, positions, locations): start = time.time() self.fill_data() @@ -235,8 +231,7 @@ def re_extract(text, pattern): def get_job_page(self, jobID): - #root = 'www.linkedin.com' - #if root not in job: + job = 'https://www.linkedin.com/jobs/view/'+ str(jobID) self.browser.get(job) self.job_page = self.load_page(sleep=0.5) @@ -263,7 +258,6 @@ def is_present(button_locator): try: time.sleep(random.uniform(1.5, 2.5)) - #print(f"Navigating... ") next_locater = (By.CSS_SELECTOR, "button[aria-label='Continue to next step']") review_locater = (By.CSS_SELECTOR, @@ -395,7 +389,7 @@ def finish_apply(self): print(parameters) - #cover_letter_loctn = parameters.get('cover_letter_loctn', [None])[0] + output_filename = [f for f in parameters.get('output_filename', ['output.csv']) if f != None] output_filename = output_filename[0] if len(output_filename) > 0 else 'output.csv' blacklist = parameters.get('blacklist', []) From 4a32c68a4b6f6fcd578eac0bc7c4e153a762855d Mon Sep 17 00:00:00 2001 From: krapes Date: Fri, 17 Jul 2020 17:48:09 -0400 Subject: [PATCH 6/7] Documentation: update README --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e5e0206..310aa7b 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,10 @@ locations: - # Location you want to search for - # A second location you want to search in -cover_letter_loctn: -- # '/home/PATH_TO_FILE' +uploads: + Resume: # PATH TO Resume + Cover Letter: # PATH TO cover letter + Photo: # PATH TO photo output_filename: - # PATH TO OUTPUT FILE (default output.csv) @@ -37,6 +39,11 @@ blacklist: ``` __NOTE: AFTER EDITING SAVE FILE, DO NOT COMMIT FILE__ +### Uploads + +There is no limit to the number of files you can list in the uploads section. +The program takes the titles from the input boxes and tries to match them with +list in the config file. ## Execute From 127bc61b8dac984b0899848332623b1b7643a2d2 Mon Sep 17 00:00:00 2001 From: Kerri Rapes Date: Sun, 19 Jul 2020 10:09:09 -0400 Subject: [PATCH 7/7] Create stale.yml Create workflow to tag stale issues --- .github/workflows/stale.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..3404517 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,19 @@ +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'Stale issue message' + stale-pr-message: 'Stale pull request message' + stale-issue-label: 'no-issue-activity' + stale-pr-label: 'no-pr-activity'