Skip to content

Commit

Permalink
merged with main
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-sussman committed Jun 27, 2024
2 parents 8291dfd + 152b169 commit 71f82a4
Show file tree
Hide file tree
Showing 74 changed files with 158,575 additions and 14,205 deletions.
26 changes: 13 additions & 13 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ GEM
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.2)
autoprefixer-rails (10.4.16.0)
execjs (~> 2)
avo (3.9.1)
avo (3.9.2)
actionview (>= 6.1)
active_link_to
activerecord (>= 6.1)
Expand All @@ -100,13 +100,13 @@ GEM
view_component (>= 3.7.0)
zeitwerk (>= 2.6.12)
aws-eventstream (1.3.0)
aws-partitions (1.944.0)
aws-sdk-core (3.197.0)
aws-partitions (1.946.0)
aws-sdk-core (3.197.2)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.84.0)
aws-sdk-kms (1.85.0)
aws-sdk-core (~> 3, >= 3.197.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.152.3)
Expand Down Expand Up @@ -207,7 +207,7 @@ GEM
railties (>= 5.0.0)
faker (3.4.1)
i18n (>= 1.8.11, < 2)
faraday (2.9.1)
faraday (2.9.2)
faraday-net_http (>= 2.0, < 3.2)
faraday-follow_redirects (0.3.0)
faraday (>= 1, < 3)
Expand Down Expand Up @@ -302,7 +302,7 @@ GEM
mime-types (1.25.1)
mini_histogram (0.3.1)
mini_mime (1.1.5)
minitest (5.23.1)
minitest (5.24.0)
msgpack (1.7.2)
multi_xml (0.7.1)
bigdecimal (~> 3.1)
Expand All @@ -319,7 +319,7 @@ GEM
timeout
net-smtp (0.5.0)
net-protocol
newrelic_rpm (9.10.2)
newrelic_rpm (9.11.0)
nio4r (2.7.3)
nokogiri (1.16.6-aarch64-linux)
racc (~> 1.4)
Expand All @@ -334,7 +334,7 @@ GEM
nokogiri (1.16.6-x86_64-linux)
racc (~> 1.4)
orm_adapter (0.5.0)
pagy (8.4.4)
pagy (8.4.5)
parallel (1.25.1)
parser (3.3.3.0)
ast (~> 2.4.1)
Expand All @@ -346,7 +346,7 @@ GEM
popper_js (2.11.8)
psych (5.1.2)
stringio
public_suffix (5.1.1)
public_suffix (6.0.0)
puma (6.4.2)
nio4r (~> 2.0)
raabro (1.4.0)
Expand Down Expand Up @@ -428,7 +428,7 @@ GEM
rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (6.1.2)
rspec-rails (6.1.3)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
Expand Down Expand Up @@ -531,7 +531,7 @@ GEM
thor (1.3.1)
tilt (2.3.0)
timeout (0.4.1)
tinymce-rails (7.1.2.1)
tinymce-rails (7.2.0)
railties (>= 3.1.1)
turbo-rails (2.0.5)
actionpack (>= 6.0.0)
Expand Down
39 changes: 0 additions & 39 deletions app/models/company.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,11 @@ class Company < ApplicationRecord
# multisearchable against: [:name]

# == Callbacks ============================================================
# TODO: Decide if we want to keep these callbacks
before_save :set_website_url, :fetch_description

# == Class Methods ========================================================

# == Instance Methods =====================================================

# TODO: Refactor this into a service class as shouldn't sit in the model
def create_all_relevant_jobs
jobs_found_or_created = []
ats = applicant_tracking_system
all_jobs = ats.fetch_company_jobs(ats_identifier)
raise Errors::NoDataReturnedError, "The API returned no jobs data for #{name}" unless all_jobs

all_jobs.each do |job_data|
details = ats.fetch_title_and_location(job_data)
next unless relevant?(*details)

# create jobs with data from ATS company endpoint unless individual job endpoint exists:
if ats.individual_job_endpoint_exists?
job_id = ats.fetch_id(job_data)
job = JobCreator.call(ats:, company: self, job_id:)
else
job = JobCreator.call(ats:, company: self, data: job_data)
end
jobs_found_or_created << job if job&.persisted?
end
puts "Found or created #{jobs_found_or_created.size} new jobs with #{name}."
jobs_found_or_created
end

def short_description
return if industry == 'n/a'

Expand All @@ -61,8 +35,6 @@ def url_present?(url_type)
send(url_type).present?
end

private

# TODO: Have identified these as the primary drivers of slow test speed due to API calls. Need to decide on next steps.
# TODO: This is a very hacky temporary solution to speed up tests. Need to fix this
# TODO: Remove these as depenedencies - we will find another way to do this
Expand All @@ -78,17 +50,6 @@ def set_website_url
end
end

def fetch_description
return if description.present?

if Rails.env.production?
inferred_description, @name_keywords = Categorizer::CompanyDescriptionService.lookup_company(name, ats_identifier)
self.description = inferred_description if description.blank?
else
self.description = "A financial services company."
end
end

# def fetch_industry
# return unless industry == 'n/a'

Expand Down
2 changes: 1 addition & 1 deletion app/models/concerns/ats/company_creator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def company_details(ats_identifier, data = nil)
end

def company_details_from_data(data)
refer_to_module(defined?(super) ? super : nil, __method__)
data
end

def fetch_company_id(data)
Expand Down
11 changes: 11 additions & 0 deletions app/models/concerns/ats/workday/application_fields.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Ats
module Workday
module ApplicationFields
def get_application_criteria(job, _data)
p "Getting Workday application criteria"
job.application_criteria = {} # TODO: fix this
job.save
end
end
end
end
122 changes: 122 additions & 0 deletions app/models/concerns/ats/workday/company_details.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
module Ats
module Workday
module CompanyDetails
def fetch_subsidiaries(company)
ats_identifier = company.ats_identifier
return if ats_identifier.count('/') > 2

puts "Checking if the company has subsidiaries..."
data = fetch_company_jobs(ats_identifier, facets_only: true)
company_facet = data&.find { |f| f['descriptor']&.match?(/compan(y|ies)/i) }
return unless company_facet

company_facet['values'].map do |company_data|
data = {
name: company_data['descriptor'],
ats_identifier: "#{company.ats_identifier}/#{company_data['id']}"
}
find_or_create_company_by_data(data)
end
end

# get name from job
# get description from sidebar_data
# get website from approot/sidebar
# website -> Crunchbase -> more reliable name and description

def company_details(ats_identifier)
api_base, url_ats_main, tenant = parse_identifier(ats_identifier)
return {} unless api_base && url_ats_main && tenant

sidebar_api_url, approot_api_url, url_ats_api = build_endpoints(api_base)

sidebar_data = get_json_data(sidebar_api_url)
approot_data = get_json_data(approot_api_url)
return {} unless sidebar_data || approot_data

url_website = approot_data&.dig('header', 'homePageURL')
name, website, total_live = fetch_details_from_jobs_endpoint(ats_identifier)
url_website = website unless url_website.present?

{
name: name || tenant.capitalize,
description: Flipper.enabled?(:company_description) ? fetch_company_description(sidebar_data) : 'Not added yet',
url_ats_api:,
url_ats_main:,
url_careers: check_for_careers_url_redirect(url_ats_main),
url_website:,
url_linkedin: fetch_linkedin(approot_data),
total_live:
# can get logo_url from sidebar_data
}
end

def fetch_company_id(data)
data[:ats_identifier]
end

private

def check_for_careers_url_redirect(url_ats_main)
url_ats_main
end

def fetch_base_url(ats_identifier)
parse_identifier(ats_identifier).first
end

def parse_identifier(ats_identifier)
tenant, site_id, version = ats_identifier.split('/')
return unless tenant && site_id

version ||= 1
base_url = url_api.gsub('XXX', tenant).sub('YYY', site_id).sub('ZZZ', version)
url_ats_main = url_base.sub('XXX', tenant).sub('YYY', site_id).sub('ZZZ', version)
[base_url, url_ats_main, tenant]
end

def build_endpoints(api_base)
sidebar_api_url = "#{api_base}sidebar"
approot_api_url = "#{api_base}approot"
url_ats_api = "#{api_base}jobs"
[sidebar_api_url, approot_api_url, url_ats_api]
end

def fetch_details_from_jobs_endpoint(ats_identifier)
data = fetch_company_jobs(ats_identifier, one_job_only: true)
job_data = data['jobPostings']&.first
total_live = data['total']

name, website = fetch_details_from_job(ats_identifier, job_data)
[name, website, total_live]
end

def fetch_details_from_job(ats_identifier, job_data)
job_id = fetch_id(job_data)
data = fetch_detailed_job_data(ats_identifier, job_id)
return unless data

name = data.dig('hiringOrganization', 'name')
website = data.dig('hiringOrganization', 'url')
[name, website]
end

def fetch_company_description(sidebar_data)
return unless sidebar_data

sidebar_data.find do |data|
type = data['type']&.downcase
break data['text'] if %w[text image].include?(type)
end
end

def fetch_linkedin(approot_data)
return unless approot_data

socials = approot_data.dig('footer', 'socialLinksList')
linkedin = socials&.find { |e| e['label'] == 'LinkedIn' }
linkedin&.dig('uri')
end
end
end
end
77 changes: 77 additions & 0 deletions app/models/concerns/ats/workday/fetch_company_jobs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
module Ats
module Workday
module FetchCompanyJobs
include Constants

def fetch_company_jobs(ats_identifier, one_job_only: false, facets_only: false)
api_base = fetch_base_url(ats_identifier)
limit = 20
initial_parameters = { endpoint: "#{api_base}jobs", limit: }
subsidiary_id = ats_identifier.split('/').last if ats_identifier.count('/') > 2

data = fetch_chunk(initial_parameters) # get facets array to build full_parameters

location_parameters = build_facet(data['facets'], [/location/i, /countr(y|ies)/i], JOB_LOCATION_KEYWORDS.excluding(/remote/))
company_parameters = build_facet(data['facets'], [/compan(y|ies)/i], [subsidiary_id], 'id') if subsidiary_id
full_parameters = {
location_parameters:,
company_parameters:
}.merge(initial_parameters)

data = fetch_chunk(full_parameters) # fetch first tranche of jobs
return data if one_job_only
return data['facets'] if facets_only

total_live = data['total']
return unless total_live&.positive?

puts "Couldn't find a location parameter. Returning all jobs regardless of location." unless location_parameters.present?
jobs = data['jobPostings']

page = 1
while page * limit < total_live
data = fetch_chunk(full_parameters, page) # fetch additional tranches
jobs += data['jobPostings']
page += 1
end
jobs
end

private

def fetch_chunk(params, page = 0)
puts "fetching jobs from #{params[:endpoint]}, page##{page + 1}..."
limit = params[:limit] || 20
request_body = {
limit:,
offset: page * limit,
appliedFacets: {},
searchText: ""
}
request_body[:appliedFacets].merge!(params[:location_parameters]) if params[:location_parameters]
request_body[:appliedFacets].merge!(params[:company_parameters]) if params[:company_parameters]
p request_body
json_post_request(params[:endpoint], request_body)
end

def build_facet(facets, parameter_array, descriptor_array, descriptor_field = 'descriptor')
queue = [[facets, nil]]
parameters = Hash.new { |hash, key| hash[key] = [] }

until queue.empty? || parameters.present? # returns once it finds a single valid parameter
current_level, facet_parameter = queue.shift

current_level.each do |facet|
if facet.is_a?(Hash) && facet['values']
queue << [facet['values'], facet['facetParameter'].to_sym] if parameter_array.any? { |parameter| facet['facetParameter']&.match?(parameter) } || parameter_array.any? { |parameter| facet['descriptor']&.match?(parameter) }
elsif facet[descriptor_field] && descriptor_array.any? { |descriptor| facet[descriptor_field].match?(descriptor) }
parameters[facet_parameter] << facet['id']
end
end
end

parameters
end
end
end
end
Loading

0 comments on commit 71f82a4

Please sign in to comment.