Finalize houdini_full_contact
This commit is contained in:
parent
a3b2e9f954
commit
7d8698a502
11 changed files with 72 additions and 82 deletions
|
@ -5,7 +5,8 @@
|
||||||
|
|
||||||
Wisper.clear if Rails.env.development?
|
Wisper.clear if Rails.env.development?
|
||||||
|
|
||||||
Rails.application.config.houdini.listeners << [NonprofitMailerListener,
|
Rails.application.config.houdini.listeners.push(NonprofitMailerListener,
|
||||||
CreditCardPaymentListener,
|
CreditCardPaymentListener,
|
||||||
SepaPaymentListener,
|
SepaPaymentListener,
|
||||||
TicketListener]
|
TicketListener
|
||||||
|
)
|
||||||
|
|
|
@ -104,7 +104,7 @@ in the provided locales: #{Houdini.intl.available_locales.join(', ')}") if Houdi
|
||||||
Houdini.show_state_fields = app.config.houdini.show_state_fields
|
Houdini.show_state_fields = app.config.houdini.show_state_fields
|
||||||
Houdini.default_bp = app.config.houdini.default_bp.id
|
Houdini.default_bp = app.config.houdini.default_bp.id
|
||||||
|
|
||||||
Houdini.event_publisher.subscribe_all(app.config.houdini.listeners.flatten)
|
Houdini.event_publisher.subscribe_all(app.config.houdini.listeners)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
# FullContact
|
# FullContact
|
||||||
An Houdini add-on to use FullContact's Context API
|
An Houdini add-on to use FullContact's Enrich API. This add-on provides a few features:
|
||||||
|
|
||||||
|
* a event listener for supporter_create which downloads information from the Enrich API.
|
||||||
|
* adds a has_many relation on Supporter for every set of data about that Supporter downloaded.
|
||||||
|
Each item is an instance of `Houdini::FullContact::Info`
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
How to use my plugin.
|
You can provide your FullContact API key in one of two ways:
|
||||||
|
|
||||||
|
* Setting the `FULL_CONTACT_KEY` environment variable or
|
||||||
|
* Setting the `houdini.full_contact.api_key` configuration option
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Add this line to your application's Gemfile:
|
Add this line to your application's Gemfile:
|
||||||
|
@ -16,10 +23,7 @@ And then execute:
|
||||||
$ bundle
|
$ bundle
|
||||||
```
|
```
|
||||||
|
|
||||||
Or install it yourself as:
|
And then install the database migrations for houdini_full_contact:
|
||||||
```bash
|
```bash
|
||||||
$ gem install full_contact
|
bin/rails houdini_full_contact:install:migrations
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributing
|
|
||||||
Contribution directions go here.
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
|
||||||
|
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
|
||||||
|
class Houdini::FullContact::ApplicationJob < ActiveJob::Base
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
|
||||||
|
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
|
||||||
|
class Houdini::FullContact::FullContactJob < Houdini::FullContact::ApplicationJob
|
||||||
|
queue_as :full_contact_queue
|
||||||
|
|
||||||
|
retry_on Exception, wait: ->(executions) { executions **2.195 }, attempts: Houdini::FullContact.max_attempts || 1
|
||||||
|
|
||||||
|
def perform(supporter)
|
||||||
|
Houdini::FullContact::InsertInfos.single(supporter)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,13 @@
|
||||||
module Houdini::FullContact::FullContactListener
|
# frozen_string_literal: true
|
||||||
def supporter_create(supporter)
|
|
||||||
Houdini::FullContact::InsertInfos.enqueue(supporter.id)
|
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
|
||||||
|
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
|
||||||
|
class Houdini::FullContact::FullContactListener
|
||||||
|
def name
|
||||||
|
self.class.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.supporter_create(supporter)
|
||||||
|
FullContactJob.perform_later(supporter)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -5,5 +5,5 @@ module Houdini::FullContact
|
||||||
|
|
||||||
autoload :InsertInfos
|
autoload :InsertInfos
|
||||||
|
|
||||||
mattr_accessor :api_key
|
mattr_accessor :api_key, :max_attempts
|
||||||
end
|
end
|
|
@ -7,6 +7,7 @@ module Houdini::FullContact
|
||||||
config.generators.api_only = true
|
config.generators.api_only = true
|
||||||
|
|
||||||
config.houdini.full_contact = ActiveSupport::OrderedOptions.new
|
config.houdini.full_contact = ActiveSupport::OrderedOptions.new
|
||||||
|
config.houdini.full_contact.max_attempts = 5
|
||||||
|
|
||||||
initializer 'houdini.full_contact.supporter_extension' do
|
initializer 'houdini.full_contact.supporter_extension' do
|
||||||
ActiveSupport.on_load(:houdini_supporter) do
|
ActiveSupport.on_load(:houdini_supporter) do
|
||||||
|
@ -18,6 +19,7 @@ module Houdini::FullContact
|
||||||
config.before_initialize do |app|
|
config.before_initialize do |app|
|
||||||
Houdini::FullContact.api_key = app.config.houdini.full_contact.api_key ||
|
Houdini::FullContact.api_key = app.config.houdini.full_contact.api_key ||
|
||||||
ENV.fetch('FULL_CONTACT_KEY')
|
ENV.fetch('FULL_CONTACT_KEY')
|
||||||
|
Houdini::FullContact.max_attempts = app.config.houdini.full_contact.max_attempts
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,60 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
|
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
|
||||||
require 'qx'
|
require 'qx'
|
||||||
require 'rest-client'
|
require 'rest-client'
|
||||||
module Houdini::FullContact::InsertInfos
|
module Houdini::FullContact::InsertInfos
|
||||||
# Work off of the full_contact_jobs queue
|
|
||||||
def self.work_queue
|
|
||||||
ids = Houdini::FullContact::Job.pluck(:supporter_id)
|
|
||||||
Houdini::FullContact::Job.delete_all
|
|
||||||
self.bulk(ids) if ids.any?
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Enqueue full contact jobs for a set of supporter ids
|
|
||||||
def self.enqueue(supporter_ids)
|
|
||||||
Qx.insert_into(:full_contact_jobs)
|
|
||||||
.values(supporter_ids.map{|id| {supporter_id: id}})
|
|
||||||
.ex
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# We need to throttle our requests by 10ms since that is our rate limit on FullContact
|
|
||||||
def self.bulk(supporter_ids)
|
|
||||||
created_ids = []
|
|
||||||
supporter_ids.each do |id|
|
|
||||||
now = Time.current
|
|
||||||
result = self.single id
|
|
||||||
created_ids.push(GetData.hash(result, 'full_contact_info', 'id')) if result.is_a?(Hash)
|
|
||||||
interval = 0.1 - (Time.current - now) # account for time taken in .single
|
|
||||||
sleep interval if interval > 0
|
|
||||||
end
|
|
||||||
return created_ids
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Fetch and persist a single full contact record for a single supporter
|
# Fetch and persist a single full contact record for a single supporter
|
||||||
# return an exception if 404 or something else went poop
|
# return an exception if 404 or something else went poop
|
||||||
def self.single(supporter_id)
|
# @param supporter Supporter
|
||||||
supporter = Houdini.core_classes.fetch(:supporter).constantize
|
def self.single(supporter)
|
||||||
supp = supporter.find(supporter_id)
|
return if supporter.nil? || supporter.email.blank?
|
||||||
return if supp.nil? || supp.email.blank?
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
response = RestClient.post("https://api.fullcontact.com/v3/person.enrich",
|
response = RestClient.post("https://api.fullcontact.com/v3/person.enrich",
|
||||||
{
|
{
|
||||||
"email" => supp.email,
|
"email" => supporter.email,
|
||||||
}.to_json,
|
}.to_json,
|
||||||
{
|
{
|
||||||
:authorization => "Bearer #{Houdini::FullContact.api_key}",
|
:authorization => "Bearer #{Houdini::FullContact.api_key}",
|
||||||
"Reporting-Key" => supp.nonprofit_id
|
"Reporting-Key" => supporter.nonprofit_id
|
||||||
})
|
})
|
||||||
result = JSON.parse(response.body)
|
result = JSON.parse(response.body)
|
||||||
rescue Exception => e
|
rescue RestClient::NotFound => e
|
||||||
return e
|
# this means there's no information about this contact so there's nothing to do.
|
||||||
|
# We just return and end
|
||||||
|
|
||||||
|
# NOTE: We pass on other errors because that means something failed. *shrug*
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
location = result['location'] && result['details']['locations'] && result['details']['locations'][0]
|
location = result['location'] && result['details']['locations'] && result['details']['locations'][0]
|
||||||
existing = supp.full_contact_info
|
existing = supporter.full_contact_infos.last
|
||||||
info_data = {
|
info_data = {
|
||||||
full_name: result['fullName'],
|
full_name: result['fullName'],
|
||||||
gender: result['gender'],
|
gender: result['gender'],
|
||||||
|
@ -64,7 +38,13 @@ module Houdini::FullContact::InsertInfos
|
||||||
age_range: result['ageRange'],
|
age_range: result['ageRange'],
|
||||||
location_general: result['location'],
|
location_general: result['location'],
|
||||||
websites: ((result['details'] && result['details']['urls']) || []).map{|h| h['value']}.join(','),
|
websites: ((result['details'] && result['details']['urls']) || []).map{|h| h['value']}.join(','),
|
||||||
supporter_id: supporter_id
|
supporter_id: supporter.id
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'full_contact_info' => full_contact_info,
|
||||||
|
'full_contact_photos' => full_contact_photos,
|
||||||
|
'full_contact_social_profiles' => full_contact_social_profiles,
|
||||||
|
'full_contact_orgs' => full_contact_orgs
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing
|
if existing
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
|
|
||||||
desc 'For generating Full Contact data'
|
|
||||||
|
|
||||||
# Clear old activerecord sessions tables daily
|
|
||||||
namespace :houdini do
|
|
||||||
namespace :full_contact do
|
|
||||||
task work_queue: :environment do
|
|
||||||
loop do
|
|
||||||
sleep(10) until Qx.select('COUNT(*)').from('full_contact_jobs').execute.first['count'] > 0
|
|
||||||
puts 'working...'
|
|
||||||
|
|
||||||
begin
|
|
||||||
Houdini::FullContact::InsertInfos.work_queue
|
|
||||||
rescue Exception => e
|
|
||||||
puts "Exception thrown: #{e}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -41,7 +41,6 @@ module InsertSupporter
|
||||||
|
|
||||||
# GeocodeModel.delay.supporter(supporter['id'])
|
# GeocodeModel.delay.supporter(supporter['id'])
|
||||||
Houdini.event_publisher.announce(:supporter_create, Supporter.find(supporter['id']))
|
Houdini.event_publisher.announce(:supporter_create, Supporter.find(supporter['id']))
|
||||||
Houdini::FullContact::InsertInfos.enqueue([supporter['id']])
|
|
||||||
|
|
||||||
supporter
|
supporter
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue