-
Notifications
You must be signed in to change notification settings - Fork 2
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
08/07/24 form filler #214
Merged
Merged
08/07/24 form filler #214
Changes from 11 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
38528d6
created DevitFormFiller subclass
daniel-sussman 505aa44
created form_filler and devit_form_filler classes
daniel-sussman 7bfb58d
removed some unnecessary code
daniel-sussman 2ec2dbc
JB feedback
daniel-sussman efe54af
wrote gh_form_filler subclass
daniel-sussman ed06f61
configured gh_form_filler to use numerical locators
daniel-sussman 54958f4
gh_form_filler can handle dropdown fields
daniel-sussman 703e37e
gh_form_filler can handle all the codepath questions except compliance
daniel-sussman 3a40e56
ashby_form_filler can handle location questions
daniel-sussman a823bec
added job_api_examples
daniel-sussman 99ac85e
moved job_api_examples
daniel-sussman de1e934
Added more methods to ashby_form_filler; works with a complex applica…
daniel-sussman 31b866c
added LoggingHelper
daniel-sussman a82fb63
modified LoggingHelper to log the name of the method passed to it
daniel-sussman 83c5832
merged main
daniel-sussman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# frozen_string_literal: true | ||
|
||
module Applier | ||
class AshbyFormFiller < FormFiller | ||
def initialize(payload = sample_payload) | ||
super | ||
end | ||
|
||
def attach_file_to_application | ||
hidden_element.attach_file(@filepath) | ||
end | ||
|
||
def click_submit_button | ||
sleep 2 # temporary -- just for testing | ||
p "I didn't actually submit the form." | ||
end | ||
|
||
def handle_location | ||
input_field = find(:css, "label[for='#{@locator}']").sibling('input') | ||
@value.chars.each do |char| | ||
input_field.send_keys(char) | ||
break page.document.find('div', exact_text: @value).click if page.document.has_selector?('div', exact_text: @value) | ||
end | ||
sleep 0.1 # Otherwise the pop-up menu obscures the next field | ||
end | ||
|
||
def hidden_element | ||
find("##{@locator}", visible: false) | ||
end | ||
|
||
def sample_payload | ||
{ | ||
user_fullname: 'John Smith', | ||
apply_url: 'https://jobs.ashbyhq.com/lightdash/9efa292a-cc34-4388-90a2-2bed5126ace4', | ||
form_locator: '#form', | ||
fields: [ | ||
{ | ||
locator: '_systemfield_name', | ||
interaction: :input, | ||
value: 'John Smith' | ||
}, | ||
{ | ||
locator: '_systemfield_email', | ||
interaction: :input, | ||
value: 'j.smith@example.com' | ||
}, | ||
{ | ||
locator: '00415714-75f3-49b9-b856-ac674fd5ce8b', | ||
interaction: :location, | ||
value: 'London, Greater London, England, United Kingdom' | ||
}, | ||
{ | ||
locator: '36913b1f-c34f-4693-919c-400304a2a11d', | ||
interaction: :input, | ||
value: 'https://www.linkedin.com/in/my_profile' | ||
}, | ||
{ | ||
locator: 'f93bff8c-2442-42b7-b040-3876fa160aba', | ||
interaction: :input, | ||
value: "This is a very good company to work for. Fantastic reviews on Glassdoor. I have long dreamed of applying to work at this company. You do amazing and innovative things!" | ||
}, | ||
{ | ||
locator: '_systemfield_resume', | ||
interaction: :upload, | ||
value: File.open('public/Obretetskiy_cv.pdf') | ||
} | ||
] | ||
} | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# frozen_string_literal: true | ||
|
||
module Applier | ||
class DevitFormFiller < FormFiller | ||
def initialize(payload = sample_payload) | ||
super | ||
end | ||
|
||
def submit_button | ||
first(:button, text: /\bsend\b/i) || first(:link, text: /\bsend\b/i) | ||
end | ||
|
||
def sample_payload | ||
{ | ||
user_fullname: 'John Smith', | ||
apply_url: 'https://devitjobs.uk/jobs/Critical-Software-Software-Engineer', | ||
form_locator: 'form', | ||
fields: [ | ||
{ | ||
locator: 'name', | ||
interaction: :input, | ||
value: 'John Smith' | ||
}, | ||
{ | ||
locator: 'email', | ||
interaction: :input, | ||
value: 'j.smith@example.com' | ||
}, | ||
{ | ||
locator: 'isFromEurope', | ||
interaction: :radiogroup, | ||
value: 'Yes' | ||
}, | ||
{ | ||
locator: '#cvFileId', | ||
interaction: :upload, | ||
value: File.open('public/Obretetskiy_cv.pdf') | ||
}, | ||
{ | ||
locator: 'motivationLetter', | ||
interaction: :input, | ||
value: 'Thank you for considering my application. It really is an honor to apply to your company. Please hire me. I would like to work here very much. I promise to work very very hard and always get along well with my coworkers.' | ||
} | ||
] | ||
} | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,131 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'json' | ||
# require 'open-uri' | ||
# require 'htmltoword' | ||
|
||
# TODO: Handle job posting becoming closed (redirect or notification on page) | ||
module Applier | ||
# Core class for filling out forms using Capybara | ||
class FormFiller < ApplicationTask | ||
include Capybara::DSL | ||
|
||
def initialize(job_application, payload) | ||
@job_application = job_application | ||
@url = @job_application.job.posting_url | ||
@ats = @job_application.job.applicant_tracking_system | ||
@fields = JSON.parse(payload) | ||
# @user = @job_application.application_process.user | ||
@errors = nil | ||
|
||
# include the relevant ATS form_filler module | ||
include_ats_module | ||
def initialize(payload) | ||
@application_form = payload[:form_locator] | ||
@fields = payload[:fields] | ||
@session = Capybara::Session.new(:selenium) | ||
@url = payload[:apply_url] | ||
@user_fullname = payload[:user_fullname] | ||
end | ||
|
||
def call | ||
return unless processable | ||
return false unless processable | ||
|
||
process | ||
rescue StandardError => e | ||
Rails.logger.error "Error running FormFiller: #{e.message}" | ||
nil | ||
using_session(@session) do | ||
process | ||
end | ||
end | ||
|
||
private | ||
|
||
def processable | ||
@url && @fields && @job_application && @ats | ||
@url && @fields && @session | ||
end | ||
|
||
def process | ||
p "Hello from FormFiller!" | ||
submit_application | ||
visit_url | ||
fill_application_form | ||
click_submit_button | ||
verify_submission | ||
ensure | ||
@session.quit | ||
end | ||
|
||
def include_ats_module | ||
ats_name = @ats.name.gsub(/\W/, '').capitalize | ||
module_name = "Ats::#{ats_name}::SubmitApplication" | ||
extend Object.const_get(module_name) if Object.const_defined?(module_name) | ||
def apply_button | ||
find(:css, 'button, a', text: /apply/i, match: :first) | ||
end | ||
|
||
def submit_application | ||
return super if defined?(super) | ||
def attach_file_to_application | ||
find(@locator).attach_file(@filepath) | ||
end | ||
|
||
puts "Write a submit method for #{@ats.name}!" | ||
return | ||
def click_apply_button | ||
apply_button.click | ||
end | ||
|
||
# def take_screenshot_and_store(session, job_application) | ||
# screenshot_path = Rails.root.join('tmp', "screenshot-#{job_application.id}.png") | ||
# session.save_screenshot(screenshot_path) | ||
def click_submit_button | ||
sleep 2 # temporary -- just for testing | ||
submit_button.click | ||
end | ||
|
||
# file = File.open(screenshot_path) | ||
# job_app = job_application | ||
# job_app.screenshot.attach(io: file, filename: "screenshot-#{job_application.id}.png", content_type: 'image/png') | ||
def doc_tmp_file | ||
docx = Htmltoword::Document.create(@value) | ||
@filepath = Rails.root.join("tmp", "Cover Letter_#{unique_string}.docx") | ||
File.binwrite(@filepath, docx) | ||
end | ||
|
||
def fill_application_form | ||
click_apply_button | ||
fill_in_all_fields | ||
end | ||
|
||
# File.delete(screenshot_path) | ||
# end | ||
def fill_in_all_fields | ||
within(@application_form) do | ||
@fields.each { |field| fill_in_field(field) } | ||
end | ||
end | ||
|
||
def fill_in_field(field) | ||
@locator = field[:locator] | ||
@value = field[:value] | ||
send(:"handle_#{field[:interaction]}") | ||
rescue Capybara::ElementNotFound => e | ||
p e.message | ||
end | ||
|
||
def handle_input | ||
fill_in(@locator, with: @value) | ||
end | ||
|
||
def handle_radiogroup | ||
choose(option: @value, name: @locator) | ||
end | ||
|
||
def handle_multi_select | ||
within(response_field) do | ||
@value.each { |value| check(value) } | ||
end | ||
end | ||
|
||
def handle_upload | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Super nicely organised code |
||
@value.instance_of?(String) ? doc_tmp_file : pdf_tmp_file | ||
attach_file_to_application | ||
end | ||
|
||
def pdf_tmp_file | ||
@filepath = Rails.root.join("tmp", "Resume - #{unique_string}.pdf") | ||
File.binwrite(@filepath, @value) | ||
end | ||
|
||
def response_field | ||
first('label', text: @locator) | ||
rescue Capybara::ElementNotFound | ||
first('div', text: @locator) | ||
end | ||
|
||
def submit_button | ||
first(:button, text: /submit/i) || first(:link, text: /submit/i) | ||
end | ||
|
||
def timestamp | ||
Time.now.to_s.truncate(19).gsub(/\D/, '') | ||
end | ||
|
||
def unique_string | ||
"#{@user_fullname}_#{timestamp}" | ||
end | ||
|
||
def verify_submission | ||
sleep 4 # temporary -- just for testing | ||
# TODO: add logic to check for successful submission message or other indicators | ||
end | ||
|
||
def visit_url | ||
visit(@url) | ||
p "Successfully reached #{@url}" | ||
end | ||
end | ||
end |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is one to go into more detail on later with JB on how we want to handle files - he was discussing markdown for cover letters for example