Skip to content

Instantly share code, notes, and snippets.

@igor-alexandrov
Created January 15, 2021 12:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save igor-alexandrov/827115720fa7a7fcf12b70ed11c0b25f to your computer and use it in GitHub Desktop.
Save igor-alexandrov/827115720fa7a7fcf12b70ed11c0b25f to your computer and use it in GitHub Desktop.
JetRockets – Ruby
# frozen_string_literal: true
class IssuesWorker < BaseWorker
sidekiq_options queue: :github_high
sidekiq_throttle_as :github_api
def perform(params)
perform_with_style(params) do |stats|
installation_id = params.with_indifferent_access[:installation_id]
repository_id = params.with_indifferent_access[:repository_id]
rels_next_href = params.with_indifferent_access[:rels_next_href]
repository = Repository.find_by_id(repository_id)
return unless repository
installation = Installation.find_by_id(installation_id)
return unless installation&.workspace
repository.update_columns(
import_started_at: Time.now,
import_finished_at: nil
)
repository_service =
RepositoryService.new(
has_github_client: installation,
octokit_options: GithubService::OCTOKIT_PAGINATION_OPTIONS,
repository: repository,
installation: installation
)
g_issues =
if rels_next_href
repository_service.get(rels_next_href)
else
repository_service.fetch_g_issues(repository.full_name)
end
last_response = repository_service.last_response
activities =
g_issues.map { |g_issue| repository_service.process_g_issue(g_issue) }
persisted_activities = activities.flatten.compact.select(&:persisted?)
stats[:g_issues] = g_issues.size
stats[:repo] = repository.full_name
stats[:activities] = persisted_activities.size
stats[:identities] =
persisted_activities.map(&:identity).compact.uniq.size
rels_next = last_response.rels[:next]
if rels_next
IssuesWorker.perform_async(
installation_id: installation_id,
repository_id: repository_id,
rels_next_href: rels_next.href
)
elsif installation.real?
StargazersWorker.perform_async(
installation_id: installation_id,
repository_id: repository_id,
rels_next_href: nil
)
else
mark_import_finished(repository, installation)
end
end
end
end
class MatchingService
def initialize(visit_id)
@matches = Hash.new
@distances = Hash.new
@visit = Visit.find(visit_id)
# The following line will likely need to be changed in the future (to not check every active care provider, if the pool is too large):
@care_providers = CareProvider.active.where(in_service_region: true)
.select{ |provider| provider.available?(@visit.appointment_time) &&
provider.payout_account &&
provider.active_subscription?
}
@max_rating = CareProvider.maximum(:rating)
end
def process
get_distances
get_match_scores
send_requests
unless @matches.empty?
notify_matchmaking
update_visit
end
end
def send_next
get_distances
get_match_scores
remove_previously_requested
unless @matches.empty?
notify_matchmaking
update_visit
VisitRequest.create!(visit_id: @visit.id, care_provider_id: @matches[0][0])
end
end
private
def get_match_scores
@care_providers.each { |care_provider| @matches[care_provider.id] = match_score(care_provider) }
@matches = @matches.sort_by(&:last).reverse
end
def get_distances
batches = [
@care_providers.first(25),
@care_providers[25..49],
@care_providers[50..74],
@care_providers[75..99],
@care_providers[100..124],
@care_providers[101..149]
].compact
batches.each do |batch|
origin_addresses = batch.map{ |provider|
address = provider.address
address ? "#{address.street} #{address.city} #{address.state}" : provider.zip }
destination_address = "#{@visit.address.street} #{@visit.address.city} #{@visit.address.state}"
distances_response = GoogleMapsApi.new(origins: origin_addresses, destination: destination_address).fetch_response
batch.each_with_index do |care_provider, index|
begin
@distances[care_provider.id] = distances_response[:rows][index][:elements][0][:distance][:value]
rescue Exception => e
puts "Error #{e}"
next
end
end
end
@distances
end
def match_score(care_provider)
rating_score(care_provider) + newcomer_score(care_provider) + distance_weighting * distance_score(care_provider)
end
def rating_score(care_provider)
care_provider.rating / @max_rating.to_f
end
def newcomer_score(care_provider)
(Time.now - care_provider.created_at) < 30.days ? 0.05 : 0.0
end
def distance_weighting
3
end
def distance_score(care_provider)
distance = @distances.values.min / @distances[care_provider.id].to_f
distance.nan? ? 0 : distance
end
def send_requests
@matches.each do |match|
VisitRequest.create!(visit_id: @visit.id, care_provider_id: match[0])
end
end
def update_visit
@visit.update(state: Visit.states[:matched])
end
def notify_matchmaking
identity = @visit.parent.identity
# TODO: Return error instead of breaking
return if (identity.try(:empty?) || identity.nil?)
body = "Time to select your Care Provider!"
notification = TwilioParentNotification.create(body: body, identity: identity, data: {type: 'matched'})
@visit.parent.send_sms(body)
@visit.update(parent_notification_step: Visit.parent_notification_steps[:matchmaking])
end
def remove_previously_requested
while VisitRequest.where(visit_id: @visit.id).where(accepted: false).pluck(:care_provider_id).include?(@matches[0][0]) && !@matches.empty?
@matches.shift
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment