Class: HouseholdPeopleAndContributions::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/household_people_and_contributions/client.rb

Constant Summary collapse

CACHE_DIR =
"./cache"
CONFIG_DIR =
"./config"
KEY_FILE =
ENV['KEY_FILE']
OUTPUT_DIR =
"./output"
MARSHALED_ACCESS_TOKEN_FILE =
"#{CONFIG_DIR}/access_token_marshal.txt"
YAMLED_ACCESS_TOKEN_FILE =
"#{CONFIG_DIR}/access_token.yml"
JSONED_CONTRIBUTION_FILE =
"contribution_records.json"
JSONED_PEOPLE_FILE =
"people_records.json"
JSONED_GRADE_FILE =
"grade_records.json"
JSONED_HOUSEHOLDS_FILE =
"household_records.json"
HOUSEHOLD_SEARCH_PATH_PREFIX =
"/v1/Households/Search.json?include=communications&searchfor="
PEOPLE_RECORDS_PATH_TEMPLATE =
"/v1/Households/%s/People.json"
ATTRIBUTED_SCHOLAR_RECORDS_PATH_FORMAT =
"/v1/People/Search.json?id=%s&include=communications,attributes"
CONTRIBUTION_RECORDS_PATH_TEMPLATE =
"/giving/v1/contributionreceipts/search.json?householdID=%s&startReceivedDate=%s&endReceivedDate=%s"
SCHOLAR_STATUS =
"scholar"
PARENT_STATUS =
"parent"
VALID_STATUSES =
[SCHOLAR_STATUS, PARENT_STATUS]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cache = true) ⇒ Client

Returns a new instance of Client.



48
49
50
# File 'lib/household_people_and_contributions/client.rb', line 48

def initialize(cache=true)
  @cache = cache
end

Class Method Details

.help(error_message = "") ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/household_people_and_contributions/client.rb', line 3

def self.help(error_message = "")
  help_message = <<-HM
  #{error_message}

KEY_FILE=`pwd`/config/f1_keys.rb ./bin/household_people_and_contributions.rb
# OR:
KEY_FILE=`pwd`/config/f1_keys.rb irb -r "./bin/household_people_and_contributions.rb"
> hpac = HouseholdPeopleAndContributions::Client.new
> hpac.pp
> hpac.contributions_by_household
> hpac.hh
  HM
  return help_message
end

.reportObject



18
19
20
# File 'lib/household_people_and_contributions/client.rb', line 18

def self.report
  new.report
end

Instance Method Details

#access_tokenObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/household_people_and_contributions/client.rb', line 88

def access_token
  unless @access_token
    if oauth_tokens?
      @access_token = OAuth::AccessToken.new(consumer, ENV['OAUTH_TOKEN'] || F1Keys::OAUTH_TOKEN, ENV['OAUTH_SECRET'] || F1Keys::OAUTH_SECRET)
    end
    unless @access_token
      if File.exists?(MARSHALED_ACCESS_TOKEN_FILE)
        @access_token = Marshal::load(File.read(MARSHALED_ACCESS_TOKEN_FILE))
        # @access_token = YAML::load(File.read(YAMLED_ACCESS_TOKEN_FILE))
      end
    end
    unless @access_token
      # @request_token = @consumer.get_request_token('Authorization' => F1Keys::BODY)
      # @request_token = @consumer.get_request_token({}, F1Keys::BODY)
      # @request_token = @consumer.get_request_token({}, 'Authorize' => F1Keys::BODY)
      # @request_token = @consumer.get_request_token({request_body: F1Keys::BODY})
      @request_token = consumer.get_request_token()

      warn @request_token.token
      warn @request_token.secret

      warn "\nPaste URL into your browser:\n\n"

      warn @request_token.authorize_url

      warn "\nPress Enter when Done\n\n"

      gets

      @access_token = @request_token.get_access_token

      warn "Access Token:"
      warn @access_token.inspect
      warn "\nDone inspecting...\n"

      File.write(MARSHALED_ACCESS_TOKEN_FILE, Marshal.dump(@access_token))
      File.write(YAMLED_ACCESS_TOKEN_FILE, YAML.dump(@access_token))

      warn "\nAccess Token is now saved for future use"
    end
  end
  @access_token
end

#add_contribution_sum_to_households(contributions, hh) ⇒ Object



396
397
398
399
400
401
402
403
404
405
# File 'lib/household_people_and_contributions/client.rb', line 396

def add_contribution_sum_to_households(contributions, hh)
  contributions.each do |contribution_record|
    household = hh.detect { |h| h["key"] == contribution_record["household_key"] }
    if household
      household["contribution_total"] ||= 0
      household["contribution_total"] += contribution_record["amount"].to_f
    end
  end
  return hh
end

#add_email_to_people(em, pp) ⇒ Object



407
408
409
410
411
412
413
414
415
# File 'lib/household_people_and_contributions/client.rb', line 407

def add_email_to_people(em, pp)
  em.each do |parent|
    person = pp.detect { |p| p["key"] == parent["key"] }
    if person
      person["email"] = parent["email"]
    end
  end
  return pp
end

#add_grades_to_people(as, pp) ⇒ Object



417
418
419
420
421
422
423
424
425
# File 'lib/household_people_and_contributions/client.rb', line 417

def add_grades_to_people(as, pp)
  as.each do |scholar|
    person = pp.detect { |p| p["key"] == scholar["key"] }
    if person
      person["grade"] = scholar["grade"]
    end
  end
  return pp
end

#asObject

attribute the people that are scholars w/ a grade(-level)



448
449
450
451
452
453
454
455
456
# File 'lib/household_people_and_contributions/client.rb', line 448

def as
  unless @as
    @as = get_scholar_grades_for(pp, recordsPerPage: 200)
    @em = get_people_email_for(pp, recordsPerPage: 200)
    @pp = add_email_to_people(@em, pp)
    @pp = add_grades_to_people(@as, pp)
  end
  @as
end

#consumerObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/household_people_and_contributions/client.rb', line 68

def consumer
  unless @consumer
    @consumer = OAuth::Consumer.new(key, secret, {
      site: "https://#{ENV['CHURCH_CODE'] || F1Keys::CHURCH_CODE}.#{ENV['ENVIRONMENT'] || F1Keys::ENVIRONMENT}.fellowshiponeapi.com",
      request_token_path: "/v1/Tokens/RequestToken",
      authorize_path: "/v1/PortalUser/Login",
      # access_token_path: "/v1/Tokens/AccessToken",
      access_token_path: "/v1/PortalUser/AccessToken", # per some blog post

      http_method: :get, #vs. :post,
      scheme: :header, # vs. :body, # vs. :query_string,
    })
  end
  @consumer
end

#contribution_records(household_id, from = nil, upto_but_not_including = nil) ⇒ Object

may-1 -> may-1 if < may1 # jan - apr 30th …current-year if >= may1 # may - dec …next-year



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/household_people_and_contributions/client.rb', line 209

def contribution_records(household_id, from=nil, upto_but_not_including=nil)
  @contribution_records = []
  unless from
    current_time = Time.now.utc
    current_month = current_time.month

    current_year = current_time.year
    previous_year = current_year - 1
    next_year = current_year + 1

    if current_month < 5 # we're nearing the end of the cycle
      # from previous year, to current-year
      from = "#{previous_year}-05-01" # TBD: make this configurable!
      upto_but_not_including ||= "#{current_year}-05-01"
    else
      # from current year, to next-year
      from = "#{current_year}-05-01"
      upto_but_not_including ||= "#{next_year}-05-01"
    end
  end

  path_params = [household_id, from, upto_but_not_including]
  contributions_path = CONTRIBUTION_RECORDS_PATH_TEMPLATE % path_params
  contributions_path = "#{contributions_path}&recordsPerPage=200"

  contribution_results = lookup(contributions_path, JSONED_CONTRIBUTION_FILE, key: "results", prefix: path_params.join('_'))

  contribution_results.each do |results|
    if contribution_array = results["contributionReceipt"]
      @contribution_records += contribution_array.reduce([]) { |memo, contribution_record|
        tmp_record = {}
        tmp_record["key"] = contribution_record["@id"]
        tmp_record["amount"] = contribution_record["amount"]
        tmp_record["fund_key"] = contribution_record["fund"]["@id"]
        tmp_record["fund_name"] = contribution_record["fund"]["name"]
        tmp_record["household_key"] = contribution_record["household"]["@id"]
        tmp_record["received_date"] = contribution_record["receivedDate"]
        tmp_record["transmit_date"] = contribution_record["transmitDate"]
        tmp_record["created_at"] = contribution_record["createdDate"]

        tmp_record["updated_at"] = contribution_record["lastUpdatedDate"]
        memo << tmp_record.dup
        memo
      }
    end
  end
  @contribution_records
end

#contributions_by_householdObject

Search for receipts containing the householdID that is passed with this parameter. startReceivedDate = search for receipts with a received date greater than or equal to this parameter (format: yyyy-mm-dd). endReceivedDate = search for receipts with a received date less than or equal to this parameter. Must be used in conjunction with startReceivedDate (format: yyyy-mm-dd). ex. startReceivedDate=2014-12-15&endReceivedDate=2014-12-16 will give you a 24 hour period.



462
463
464
465
466
467
468
469
# File 'lib/household_people_and_contributions/client.rb', line 462

def contributions_by_household
  unless @contributions_by_household
    @contributions_by_household = get_contributions_for_households(hh)
    # merge with household data
    @hh = add_contribution_sum_to_households(@contributions_by_household, hh)
  end
  @contributions_by_household
end

#get(path, key = nil) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/household_people_and_contributions/client.rb', line 132

def get(path, key=nil)
  loop_again = true
  current_page_number = 1
  records = []
  while loop_again
    paged_path = key.nil? ? path : "#{path}&page=#{current_page_number}"

    #warn "GET'ing: #{paged_path.inspect}..."
    response =  access_token.get(paged_path)

    response_body = response.body
    #warn "BODY: #{response_body.inspect}..."
    json_body = JSON.parse(response_body)

    results = key.nil? ? json_body : json_body[key]

    records << results

    if results["@additionalPages"] && results["@additionalPages"] != "0"
      loop_again = true
      fail("odd page number: #{results['@pageNumber']}") unless results["@pageNumber"] == current_page_number.to_s
      current_page_number += 1
      sleep 5
    else
      loop_again = false
    end
  end
  return records
end

#get_contributions_for_households(households = [], from = nil, upto_but_not_including = nil) ⇒ Object



198
199
200
201
202
203
204
# File 'lib/household_people_and_contributions/client.rb', line 198

def get_contributions_for_households(households=[], from=nil, upto_but_not_including=nil)
  @contributions = []
  household_ids_for(households).each do |household_id|
    @contributions += contribution_records(household_id, from, upto_but_not_including)
  end
  return @contributions
end

#get_people_email_for(people = [], options = {}) ⇒ Object



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/household_people_and_contributions/client.rb', line 364

def get_people_email_for(people=[], options = {})
  n_at_a_time = options[:recordsPerPage] || 200
  @emailable_people = []
  #warn "\n\n\t------------------> HERE\n\n"
  parents = people.select { |person| PARENT_STATUS == person["status"].downcase }
  #warn "\n\tparents: #{parents.inspect}\n\n\n"
  extra_params = "recordsPerPage=#{n_at_a_time}"
  while !parents.empty?
    parent_id_list = parents.pop(n_at_a_time).map {|s| s["key"] }
    parent_id_list_csv = parent_id_list.join(",")
    @emailable_people += parent_records(parent_id_list_csv, extra_params: extra_params)
  end

  return @emailable_people
end

#get_people_in_households(households = []) ⇒ Object



303
304
305
306
307
308
309
# File 'lib/household_people_and_contributions/client.rb', line 303

def get_people_in_households(households=[])
  @people = []
  household_ids_for(households).each do |household_id|
    @people += people_records(household_id)
  end
  return @people
end

#get_scholar_grades_for(people = [], options = {}) ⇒ Object



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/household_people_and_contributions/client.rb', line 380

def get_scholar_grades_for(people=[], options = {})
  n_at_a_time = options[:recordsPerPage] || 200
  @attributed_scholars = []
  #warn "\n\n\t------------------> HERE\n\n"
  scholars = people.select { |person| SCHOLAR_STATUS == person["status"].downcase }
  #warn "\n\tscholars: #{scholars.inspect}\n\n\n"
  extra_params = "recordsPerPage=#{n_at_a_time}"
  while !scholars.empty?
    scholar_id_list = scholars.pop(n_at_a_time).map {|s| s["key"] }
    scholar_id_list_csv = scholar_id_list.join(",")
    @attributed_scholars += scholar_records(scholar_id_list_csv, extra_params: extra_params)
  end

  return @attributed_scholars
end

#hhObject

get all the household(s):



428
429
430
431
432
433
434
435
436
437
# File 'lib/household_people_and_contributions/client.rb', line 428

def hh
  unless @hh
    query = "%"
    path = "#{HOUSEHOLD_SEARCH_PATH_PREFIX}#{query}"
    household_records_path = "#{path}&recordsPerPage=200"
    #warn "-> Search Households for #{query}..."
    @hh = household_records(household_records_path)
  end
  @hh
end

#household_ids_for(households) ⇒ Object



299
300
301
# File 'lib/household_people_and_contributions/client.rb', line 299

def household_ids_for(households)
  households.map { |household_record| household_record["key"] }
end

#household_records(household_records_path) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/household_people_and_contributions/client.rb', line 178

def household_records(household_records_path)
  @household_records = []
  household_results = lookup(household_records_path, JSONED_HOUSEHOLDS_FILE, key: "results")

  household_results.each do |results|
    household_array = results["household"]
    @household_records += household_array.reduce([]) { |memo, household_record|
      tmp_record = {}
      tmp_record["key"] = household_record["@id"]
      tmp_record["name"] = household_record["householdName"]
      tmp_record["sort_name"] = household_record["householdSortName"]
      tmp_record["created_at"] = household_record["createdDate"]
      tmp_record["updated_at"] = household_record["lastUpdatedDate"]
      memo << tmp_record.dup
      memo
    }
  end
  @household_records
end

#keyObject



52
53
54
55
56
57
58
# File 'lib/household_people_and_contributions/client.rb', line 52

def key
  unless @key
    @key =
      ENV['CONSUMER_KEY'] || F1Keys::CONSUMER_KEY
  end
  @key
end

#lookup(records_path, cache_file, options = {}) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/household_people_and_contributions/client.rb', line 162

def lookup(records_path, cache_file, options={})
  if options.key?(:prefix) && options[:prefix]
    cache_file = options[:prefix] + "_" + cache_file
  end
  cache_file =  CACHE_DIR + "/" + cache_file
  if @cache && File.exists?(cache_file)
    results_array = JSON.parse(File.read(cache_file))
  else
    results_array = get(records_path, options[:key])
    if @cache
      File.write(cache_file, results_array.to_json)
    end
  end
  return results_array
end

#oauth_tokens?Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/household_people_and_contributions/client.rb', line 84

def oauth_tokens?
  (ENV['OAUTH_TOKEN'] || F1Keys::OAUTH_TOKEN) && (ENV['OAUTH_SECRET'] || F1Keys::OAUTH_SECRET)
end

#parent_records(id_list_csv, options = {}) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/household_people_and_contributions/client.rb', line 311

def parent_records(id_list_csv, options={})
  @parent_records = []
  emailable_parent_records_path = ATTRIBUTED_SCHOLAR_RECORDS_PATH_FORMAT % id_list_csv
  emailable_parent_records_path = "#{emailable_parent_records_path}&#{options[:extra_params]}"
  parent_results = lookup(emailable_parent_records_path, JSONED_GRADE_FILE, prefix: id_list_csv.gsub(',','_'))

  parent_results.each do |results|
    parents_array = results["results"]["person"]
    @parent_records += parents_array.reduce([]) { |memo, parent_record|

      if parent_record["communications"]
        tmp_record = {}
        communications_array = parent_record["communications"]["communication"]
        communications_array.each do |communication_entry|
          if communication_entry["communicationType"]["name"].downcase == "email"
            tmp_record["key"] = communication_entry["person"]["@id"]
            tmp_record["email"] = communication_entry["communicationValue"]
          end
        end
        memo << tmp_record.dup if tmp_record["email"]
      end
      memo
    }
  end
  @parent_records
end

#people_records(household_id) ⇒ Object



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/household_people_and_contributions/client.rb', line 261

def people_records(household_id)
  people_records_path = PEOPLE_RECORDS_PATH_TEMPLATE % household_id

  @people_records = []
  people_results = lookup(people_records_path, JSONED_PEOPLE_FILE, prefix: household_id)

  people_results.each do |results|
    people_hash = results["people"]
    if people_hash
    persons_array = people_hash["person"]
    @people_records += persons_array.reduce([]) { |memo, person_record|

      status = person_record["status"]["name"].downcase
      if VALID_STATUSES.include?(status) && household_id == person_record["@householdID"]
        tmp_record = {}

        tmp_record["key"] = person_record["@id"]
        tmp_record["household_key"] = person_record["@householdID"]
        tmp_record["status"] = status

        tmp_record["first_name"] = person_record["firstName"]
        tmp_record["last_name"] = person_record["lastName"]
        tmp_record["suffix"] = person_record["suffix"]

        tmp_record["created_at"] = person_record["createdDate"]
        tmp_record["updated_at"] = person_record["lastUpdatedDate"]

        memo << tmp_record.dup
      end
      memo
    }
    else
      warn "no person hash for household(#{household_id})'s people: #{people_hash.inspect}"
  end
  end
  @people_records
end

#ppObject

get the people in each household:



440
441
442
443
444
445
# File 'lib/household_people_and_contributions/client.rb', line 440

def pp
  unless @pp
    @pp = get_people_in_households(hh)
  end
  @pp
end

#reportObject



471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/household_people_and_contributions/client.rb', line 471

def report
  as # get people, add grades to them...
  #warn "\n--> people and scholars: "
  File.write(OUTPUT_DIR + "/" + "people.json", pp.to_json)
  puts pp.to_json

  #warn "\n--> contributions: "
  File.write(OUTPUT_DIR + "/" + "contributions.json", contributions_by_household.to_json)
  puts contributions_by_household.to_json

  #warn "--> hh: "
  File.write(OUTPUT_DIR + "/" + "households.json", hh.to_json)
  puts hh.to_json

  return {hh: @hh, pp: @pp, contributions_by_household: @contributions_by_household}
end

#scholar_records(id_list_csv, options = {}) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/household_people_and_contributions/client.rb', line 338

def scholar_records(id_list_csv, options={})
  @scholar_records = []
  attributed_scholar_records_path = ATTRIBUTED_SCHOLAR_RECORDS_PATH_FORMAT % id_list_csv
  attributed_scholar_records_path = "#{attributed_scholar_records_path}&#{options[:extra_params]}"
  scholar_results = lookup(attributed_scholar_records_path, JSONED_GRADE_FILE, prefix: id_list_csv.gsub(',','_'))

  scholar_results.each do |results|
    scholars_array = results["results"]["person"]
    @scholar_records += scholars_array.reduce([]) { |memo, scholar_record|

      tmp_record = {}
      attributes_array = scholar_record["attributes"]["attribute"]
      attributes_array.each do |attribute_entry|
        if attribute_entry["attributeGroup"]["name"].downcase == "grade"
          tmp_record["key"] = attribute_entry["person"]["@id"]
          tmp_record["grade"] = attribute_entry["attributeGroup"]["attribute"]["name"]
        end
      end
      memo << tmp_record.dup if tmp_record["grade"]
      memo
    }
  end
  @scholar_records
end

#secretObject



60
61
62
63
64
65
66
# File 'lib/household_people_and_contributions/client.rb', line 60

def secret
  unless @secret
    @secret =
      ENV['CONSUMER_SECRET'] || F1Keys::CONSUMER_SECRET
  end
  @secret
end