Skip to content

Commit

Permalink
Merge branch 'master' into merge
Browse files Browse the repository at this point in the history
  • Loading branch information
MrCredible authored Jul 22, 2020
2 parents ce405bf + 96658f0 commit fa72495
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 42 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -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'
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down
20 changes: 12 additions & 8 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
# output_filename:
# - # PATH TO OUTPUT FILE (default output.csv)

# blacklist:
# - # Company names you want to ignore
69 changes: 37 additions & 32 deletions easyapplybot.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,20 @@

class EasyApplyBot:

MAX_SEARCH_TIME = 10*60

def __init__(self,
username,
password,
cover_letter_loctn=None,
uploads={},
filename='output.csv',
blacklist=[]):

log.info("Welcome to Easy Apply Bot\n")
dirpath = os.getcwd()
log.info("current directory is : " + dirpath)

self.cover_letter_loctn = cover_letter_loctn
self.appliedJobIDs = self.get_appliedIDs(filename) if self.get_appliedIDs(filename) != None else []
self.uploads = uploads
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
Expand All @@ -55,13 +54,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))]
Expand Down Expand Up @@ -101,13 +99,11 @@ def start_linkedin(self,username,password):
except TimeoutException:
log.info("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()
Expand Down Expand Up @@ -176,7 +172,6 @@ def applications_loop(self, position, location):
jobIDs = [x for x in IDs if x not in self.appliedJobIDs]
after = len(jobIDs)


if len(jobIDs) == 0 and len(IDs) > 24:
jobs_per_page = jobs_per_page + 25
count_job = 0
Expand Down Expand Up @@ -265,10 +260,9 @@ 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) + '/'
log.info("Opening Job Page \n %s", job)

job = 'https://www.linkedin.com/jobs/view/'+ str(jobID)

self.browser.get(job)
self.job_page = self.load_page(sleep=0.5)
return job, self.job_page
Expand Down Expand Up @@ -299,6 +293,7 @@ def is_present(button_locator):
return (len(self.browser.find_elements(button_locator[0], button_locator[1])) > 0)

try:

time.sleep(3)
log.info("Attempting to send resume")
#TODO These locators are not future proof. These labels could easily change. Ideally we would search for contained text;
Expand All @@ -311,6 +306,7 @@ def is_present(button_locator):
error_locator = (By.CSS_SELECTOR, "p[data-test-form-element-error-message='true']")
cover_letter = (By.CSS_SELECTOR, "input[name='file']")


testLabel_locator = (By.XPATH, "//span[@data-test-form-element-label-title='true']")
yes_locator = (By.XPATH, "//input[@value='Yes']")
no_locator = (By.XPATH, "//input[@value='No']")
Expand All @@ -323,11 +319,22 @@ def is_present(button_locator):
button = None

# Upload Cover Letter if possible
if is_present(cover_letter):
input_button = self.browser.find_elements(cover_letter[0],
cover_letter[1])
#TODO is this cover letter the same thing as the resume upload locator?
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::*")
grandparent = sibling.find_element(By.XPATH, "..")
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))

for i, button_locator in enumerate(
Expand Down Expand Up @@ -511,6 +518,7 @@ def next_jobs_page(self, position, location, jobs_per_page):
def finish_apply(self):
self.browser.close()


def setupLogger():
dt = datetime.strftime(datetime.now(), "%m_%d_%y %H_%M_%S ")

Expand All @@ -526,6 +534,7 @@ def setupLogger():
c_handler.setFormatter(c_format)
log.addHandler(c_handler)


if __name__ == '__main__':

setupLogger()
Expand All @@ -543,22 +552,18 @@ def setupLogger():


print(parameters)
resume_loctn = parameters.get('resume_loctn')
cover_letter_loctn = parameters.get('cover_letter_loctn')
output_filename = parameters.get('output_filename')
blacklist = parameters.get('blacklist')

#default to output file if nothing was given.
if output_filename == [None]:
output_filename = "./output.csv"
if not os.path.exists(output_filename):
with open(output_filename, 'w+') as f:
writer = csv.writer(f)
writer.writerow(['DateTime', 'JobID', 'Title', 'Company', 'Attempted', 'Success'])

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
)
Expand Down

0 comments on commit fa72495

Please sign in to comment.