Exemplo n.º 1
0
 def _get_job_information(self, job: Job):
     element_job_description = self.driver.find_element(
         By.XPATH, AngelConstants.XPath.JOB_DESCRIPTION)
     element_job_company = self.driver.find_element(
         By.XPATH, AngelConstants.XPath.JOB_COMPANY)
     job.description = element_job_description.text
     job.company = element_job_company.get_attribute('text').strip()
Exemplo n.º 2
0
    def answer_all_questions(self, driver: webdriver.Chrome, job: Job,
                             dict_qle: Dict[str, QuestionLabelElements]):
        # Initialize
        names = list(dict_qle.keys())
        i = 0
        name = names[i]
        list_continues: List[FirefoxWebElement] = driver.find_elements(
            By.XPATH, IndeedConstants.XPath.BUTTON_CONT)

        while True:
            qle = dict_qle[name]
            state = self._answer_question(driver, job, qle)
            if state == self.AnswerState.CANNOT_ANSWER:
                job.error = RobotConstants.String.UNABLE_TO_ANSWER
                return False

            if state == self.AnswerState.NOT_VISIBLE:
                for element_continue in list_continues:
                    try:
                        element_continue.click()
                        break
                    except common.exceptions.ElementNotVisibleException as e:
                        pass
                    except common.exceptions.NoSuchElementException as e:
                        job.error = str(e)
                        return False

            else:
                if i == len(names) - 1:
                    try:
                        driver.find_element(
                            By.XPATH,
                            IndeedConstants.XPath.BUTTON_APPLY).click()
                        return True
                    except common.exceptions.NoSuchElementException as e:
                        job.error = str(e)
                        return False
                    except common.exceptions.ElementNotVisibleException as e:
                        # TODO: Figure out why this happens
                        driver.find_element(
                            By.XPATH,
                            IndeedConstants.XPath.BUTTON_CONT).click()
                        i -= 1

                i += 1
                name = names[i]
Exemplo n.º 3
0
    def search_with_api(self, params: dict):
        client = IndeedClient(publisher=self.user_config.INDEED_API_KEY)
        search_response = client.search(**params)

        total_number_hits = search_response['totalResults']
        num_loops = int(total_number_hits /
                        IndeedConstants.API.MAX_NUM_RESULTS_PER_REQUEST)
        counter_start = 0

        print('Total number of hits: {0}'.format(total_number_hits))
        count_jobs_added = 0

        for i in range(0, num_loops):
            # We can get around MAX_NUM_RESULTS_PER_REQUEST by increasing our start location on each loop!
            params['start'] = counter_start

            search_response = client.search(**params)
            list_jobs = IndeedParser.get_jobs_from_response(search_response)
            for job in list_jobs:
                try:
                    # TODO: This sucks, I'm just repeating myself...
                    Job.create(key=job.key,
                               website=job.website,
                               link=job.link,
                               title=job.title,
                               company=job.company,
                               city=job.city,
                               state=job.state,
                               country=job.country,
                               location=job.location,
                               posted_date=job.posted_date,
                               expired=job.expired,
                               easy_apply=job.easy_apply)
                    count_jobs_added += 1

                except peewee.IntegrityError as e:
                    # TODO: Can I write a custom exception that catches UNIQUE Errors but not others?
                    if 'UNIQUE' in str(e):
                        pass
                    else:
                        print(str(e))

            # Increment start
            counter_start += IndeedConstants.API.MAX_NUM_RESULTS_PER_REQUEST

        print('Added {0} new jobs'.format(count_jobs_added))
Exemplo n.º 4
0
    def _answer_question(self, driver: webdriver.Chrome, job: Job,
                         qle: QuestionLabelElements):
        # Question should already be in database at this point with updated answer hopefully
        qle.question = Question.get(
            Question.label == qle.question.label,
            Question.tag_type == qle.question.input_type)
        if qle.question.answer is not None:
            if qle.question.secondary_input_type == HTMLConstants.InputTypes.RADIO or \
                            qle.question.secondary_input_type == HTMLConstants.InputTypes.CHECK_BOX:
                return self._answer_check_button(driver, job, qle)

            elif qle.question.input_type == HTMLConstants.TagType.SELECT:
                return self._answer_select(driver, job, qle)

            else:
                return self._answer_text(driver, job, qle)

        elif qle.question.question_type == ABCs.QuestionTypes.MESSAGE:
            return self._answer_message(driver, job, qle)

        elif qle.question.question_type == ABCs.QuestionTypes.ADDITIONAL_ATTACHMENTS:
            return self.AnswerState.CONTINUE

        elif qle.question.question_type == ABCs.QuestionTypes.LOCATION:
            if self.user_config.Default.CITY in str.lower(qle.question.label):
                qle.question.answer = 'Yes'
                return self._answer_text(driver, job, qle)

        elif qle.question.question_type == ABCs.QuestionTypes.RESUME:
            qle.question.answer = self.ab_builder.generate_resume(
                job.description)
            return self._answer_text(driver, job, qle)

        elif qle.question.question_type == ABCs.QuestionTypes.EXPERIENCE:
            qle.question.answer = self.user_config.Settings.DEFAULT_EXPERIENCE
            return self._answer_text(driver, job, qle)

        else:
            best_answer = ApplicationBuilder.generate_answer_from_questions(
                qle.question)
            if best_answer is not None:
                qle.question.answer = best_answer
                if qle.question.secondary_input_type == HTMLConstants.InputTypes.TEXT or \
                        qle.question.secondary_input_type == HTMLConstants.InputTypes.FILE or \
                        qle.question.secondary_input_type == HTMLConstants.InputTypes.EMAIL or \
                        qle.question.secondary_input_type == HTMLConstants.InputTypes.PHONE:
                    return self._answer_text(driver, job, qle)
                elif qle.question.secondary_input_type == HTMLConstants.InputTypes.RADIO:
                    return self._answer_check_button(driver, job, qle)
                elif qle.question.secondary_input_type == HTMLConstants.InputTypes.SELECT_ONE:
                    return self._answer_select(driver, job, qle)

        job.error = RobotConstants.String.UNABLE_TO_ANSWER
        return self.AnswerState.CANNOT_ANSWER
Exemplo n.º 5
0
    def _answer_message(self, driver: webdriver.Chrome, job: Job,
                        qle: QuestionLabelElements) -> Enum:
        message = self.ab_builder.generate_message(job.description,
                                                   job.company)
        if message is not None:
            try:
                driver.find_element(By.NAME, qle.name).send_keys(message)
                job.message = message
                return self.AnswerState.CONTINUE

            except common.exceptions.ElementNotVisibleException as e:
                return self.AnswerState.NOT_VISIBLE

            except common.exceptions.NoSuchElementException as e:
                job.error = str(e)
            return self.AnswerState.CANNOT_ANSWER

        else:
            job.error = RobotConstants.String.NOT_ENOUGH_KEYWORD_MATCHES
            return self.AnswerState.CANNOT_ANSWER
Exemplo n.º 6
0
    def _apply_to_single_job(self, job: Job):
        """
        Assuming you are on a job page, presses the apply button and switches to the application
        IFrame. If everything is working properly it call fill_application.
        Lastly, it saves any changes made to the job table
        :param job:
        :return:
        """
        self.attempt_application(job)
        if job.easy_apply:
            self.driver.get(job.link)

            if does_element_exist(self.driver, By.XPATH,
                                  IndeedConstants.XPath.TOS_POPUP):
                self.driver.find_element(
                    By.XPATH, IndeedConstants.XPath.TOS_POPUP).click()

            try:
                # Fill job information
                job.description = self.driver.find_element(
                    By.ID, IndeedConstants.Id.JOB_SUMMARY).text

                self.driver.find_element(
                    By.XPATH, IndeedConstants.XPath.APPLY_SPAN).click()

                # Switch to application form IFRAME, notice that it is a nested IFRAME
                time.sleep(RobotConstants.WAIT_MEDIUM)
                self.driver.switch_to.frame(1)
                self.driver.switch_to.frame(0)

                self.fill_application(job)

            except common.exceptions.NoSuchElementException as e:
                job.error = str(e)
                job.expired = True
                print(e)

        else:
            pass

        job.save()
Exemplo n.º 7
0
    def _apply_single_job(self, job: Job):
        self.driver.get(job.link)
        try:
            self._get_job_information(job)

            self.attempt_application(job)

            if 'unpaid' in job.description.lower():
                job.error = RobotConstants.String.UNPAID
                self.failed_application(job)

            elif self._fill_application(job):
                self.successful_application(
                    job, dry_run=self.user_config.Settings.IS_DRY_RUN)
            else:
                self.failed_application(job)
        except Exception as e:
            job.error = str(e)
            self.failed_application(job)

        job.save()
Exemplo n.º 8
0
    def gather(self, query_parameters):
        def extract_job_key(url: str) -> Optional[str]:
            last_part_of_url = url.rsplit('/', 1)[-1]
            match = re.match(AngelConstants.Regex.JOB_KEY_FROM_URL,
                             last_part_of_url)
            if match:
                return match.group(1)
            return None

        assert self._is_authenticated()

        string_parameters = AngelBot.encode_parameters(
            dict_parameters=query_parameters)
        self.driver.get(AngelConstants.URL.JOBS + string_parameters)

        # Check if any jobs have loaded
        WebDriverWait(self.driver, AngelConstants.PauseTime.JOBS_LOADED).until(
            EC.presence_of_element_located(
                (By.XPATH, AngelConstants.XPath.JOB_LISTING_LINK)))

        scroll_infinitely(self.driver)

        elements_list = self.driver.find_elements(
            By.XPATH, AngelConstants.XPath.JOB_LISTING_LINK)
        for element in elements_list:
            job_link = element.get_attribute('href')
            job_title = element.get_attribute('innerText')
            job_key = extract_job_key(job_link)
            if job_key is None:
                pass
            else:
                try:
                    Job.create(key=job_key,
                               website=AngelConstants.WEBSITE_NAME,
                               link=job_link,
                               title=job_title)
                except peewee.IntegrityError as e:
                    print(e)
Exemplo n.º 9
0
 def _get_job_from_result(job_result: dict) -> Optional[Job]:
     if job_result['indeedApply']:
         parsed_date = datetime.strptime(job_result['date'],
                                         '%a, %d %b %Y %H:%M:%S %Z')
         job = Job(key=job_result['jobkey'],
                   website=IndeedConstants.WEBSITE_NAME,
                   link=job_result['url'],
                   title=job_result['jobtitle'],
                   company=job_result['company'],
                   city=job_result['city'],
                   state=job_result['state'],
                   country=job_result['country'],
                   location=job_result['formattedLocation'],
                   posted_date=parsed_date.date(),
                   expired=job_result['expired'],
                   easy_apply=job_result['indeedApply'])
         return job
     return None
Exemplo n.º 10
0
    def _answer_text(self, driver: webdriver.Chrome, job: Job,
                     qle: QuestionLabelElements) -> Enum:
        try:
            element = driver.find_element(By.NAME, qle.name)
            element.clear()
            element.send_keys(qle.question.answer)
            return self.AnswerState.CONTINUE

        except common.exceptions.ElementNotVisibleException as e:
            return self.AnswerState.NOT_VISIBLE

        except common.exceptions.NoSuchElementException as e:
            job.error = str(e)

        except common.exceptions.InvalidElementStateException as e:
            return self.AnswerState.NOT_VISIBLE

        return self.AnswerState.CANNOT_ANSWER
Exemplo n.º 11
0
    def _answer_select(self, driver: webdriver.Chrome, job: Job,
                       qle: QuestionLabelElements) -> Enum:
        select_name = qle.element_list[0].get_attribute(
            HTMLConstants.Attributes.NAME)
        try:
            select = Select(driver.find_element(By.NAME, select_name))
            if qle.question.secondary_input_type == HTMLConstants.InputTypes.SELECT_ONE:
                select.select_by_value(qle.question.answer)
            else:
                raise NotImplementedError
            return self.AnswerState.CONTINUE

        except common.exceptions.ElementNotVisibleException as e:
            return self.AnswerState.NOT_VISIBLE

        except common.exceptions.NoSuchElementException as e:
            job.error = str(e)

        return self.AnswerState.CANNOT_ANSWER
Exemplo n.º 12
0
    def _fill_application(self, job: Job):
        user_note = self.application_builder.generate_message(
            company=job.company, description=job.description)
        if user_note is not None:
            try:
                apply_now_element = self.driver.find_element(
                    By.CSS_SELECTOR, AngelConstants.CSSSelector.APPLY_NOW)
                apply_now_element.click()

                job.message = user_note
                element_user_note = self.driver.find_element(
                    By.XPATH, AngelConstants.XPath.USER_NOTE)
                element_user_note.send_keys(user_note)

                if not self.user_config.Settings.IS_DRY_RUN:
                    self.driver.find_element(
                        By.XPATH, AngelConstants.XPath.APPLY).click()

                # PROMPT: Select the locations you are willing to relocate to:
                if does_element_exist(self.driver, By.XPATH,
                                      AngelConstants.XPath.UNSELECTED_CITIES):
                    try:
                        unselected_elements = self.driver.find_elements(
                            By.XPATH, AngelConstants.XPath.UNSELECTED_CITIES)
                        for element in unselected_elements:
                            element.click()
                        self.driver.find_element(
                            By.XPATH, AngelConstants.XPath.DONE).click()

                    except Exception as e:
                        print(str(e))
                        job.error = str(e)

                return True

            except common.exceptions.NoSuchElementException as e:
                job.error = str(e)

            except common.exceptions.WebDriverException as e:
                if len(user_note
                       ) > AngelConstants.Constraint.MAX_LENGTH_USER_NOTE:
                    job.error = AngelConstants.Error.USER_NOTE_TOO_LONG
                else:
                    job.error = str(e)
        else:
            job.error = RobotConstants.String.NOT_ENOUGH_KEYWORD_MATCHES

        return False
Exemplo n.º 13
0
    def _answer_check_button(self, driver: webdriver.Chrome, job: Job,
                             qle: QuestionLabelElements) -> Enum:
        if qle.question.answer is not None:
            span_answers = qle.question.answer.split(',')
            span_answers = [answer.strip() for answer in span_answers]
            element_name = qle.element_list[0].get_attribute(
                HTMLConstants.Attributes.NAME)
            for span_answer in span_answers:
                try:
                    xpath_checkbox_button = IndeedConstants.XPath.compute_xpath_check_button(
                        element_name, span_answer)
                    driver.find_element(By.XPATH,
                                        xpath_checkbox_button).click()

                except common.exceptions.ElementNotVisibleException:
                    return self.AnswerState.NOT_VISIBLE

                except common.exceptions.NoSuchElementException as e:
                    job.error = str(e)
                    return self.AnswerState.CANNOT_ANSWER
            return self.AnswerState.CONTINUE
        # TODO: Check for prefilled answers
        else:
            raise NotImplementedError
Exemplo n.º 14
0
 def _create_tables():
     # Create table if not exists
     Job.create_table(fail_silently=True)
     Question.create_table(fail_silently=True)
     Person.create_table(fail_silently=True)
Exemplo n.º 15
0
 def successful_application(job: Job, dry_run=False) -> str:
     if not dry_run:
         job.applied = True
     string = 'Successfully applied to {0} with {1} at {2}'.format(job.title, job.company, job.location)
     print(string)
     return string
Exemplo n.º 16
0
 def attempt_application(job: Job) -> str:
     job.attempted = True
     job.access_date = datetime.now().date()
     string = 'Attempting application for {0} with {1} at {2}'.format(job.title, job.company, job.location)
     print(string)
     return string