trumpcare_tracker 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '0564368e53916eb11501e4dc0ca27b3b15a42764'
4
- data.tar.gz: 74f8f1b9cebfc35a49300f46c94058486d5ee1f8
3
+ metadata.gz: fa4f9aeac84d31d7ccc1b7ccec012883492782ee
4
+ data.tar.gz: ceafe378f45ea94ff7dd6fb637dc7159e7ee03ed
5
5
  SHA512:
6
- metadata.gz: 3cd07feb4102fcd6a9e404e22983fb89d5c26ee365c75e1f47292abc029ed1506e7207a7caae20af695e3c84d561f460e6ef476b7de88e14ca74004b523e2405
7
- data.tar.gz: 9fb68b6fc37bd924b85f1daf59deb6554dbe14ef42f23215d00e7adf8d315ed5a846161b0126da8d31733b0111770b734baea67163937c7887f9c0436221416d
6
+ metadata.gz: ac743a03cbb34d688c811c48b49afc9fc4dfa2c6325af6f92eb36a40a22d0ac3d9feec38b7b86b16d38dd3b345ea596870df4d291a5d2939b2cbaa5908291ca0
7
+ data.tar.gz: e5f7b6667e58b6b56f08d7c0b4ef28d16880cabe30395397881ce5f57c40069e445b308f35426a923ca30815ef3df4c8e9fb28c1b772e17d784f08ca7abf68fa
data/Rakefile CHANGED
@@ -1,120 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/gem_tasks'
4
- require 'csv'
5
- require 'nokogiri'
6
- require 'open-uri'
7
- require 'pyr'
8
- require 'trumpcare_tracker'
4
+ require 'date'
9
5
 
10
6
  require 'rubocop/rake_task'
11
7
  RuboCop::RakeTask.new
12
8
 
13
9
  task default: %i[spec rubocop]
14
10
 
15
- desc 'Output a report of Senate Democrats Trumpcare tweets'
16
- task :export do
17
- csv_tweet_report = CSV.generate do |csv|
18
- csv << %w[
19
- senator
20
- official_user_name
21
- alt_user_name
22
- tweets_in_last_seven_days
23
- trumpcare_tweets
24
- tct_percentage
25
- russia_tweets
26
- rt_percentage
27
- tct_to_rt_ratio
28
- trumpcare_tweet_urls
29
- russia_tweet_urls
30
- ]
31
- audit_tweets(reps) { |tracker| csv << tracker.to_h.values }
32
- end
33
-
34
- File.open('trumpcare_tweet_report.csv', 'w') do |file|
35
- file.write csv_tweet_report
36
- end
11
+ require 'trumpcare_tracker/tweet_bot'
12
+ TrumpcareTracker::TweetBot.new('trumpcaretrackr') do
13
+ "#{Date.today.strftime('%A, %B %d %Y')} - "\
14
+ "here's how Senate Democrats have tweeted about TrumpCare the past week. (thread)"
37
15
  end
38
16
 
39
- desc 'Search Senators\' official homepage for TrumpCare and Russia Mentions'
40
- task :scrape_homepage do
41
- csv_homepage_report = CSV.generate do |csv|
42
- csv << %w[senator url trumpcare_mentions russia_mentions trumpcare_to_russia_ratio]
43
- reps.each do |rep|
44
- doc = Nokogiri::HTML(open(rep.url))
45
- trumpcare_mentions_count = mentions_mapper(doc, TrumpcareTracker.trumpcare_keyword_regex).count
46
- russia_mentions_count = mentions_mapper(doc, TrumpcareTracker.russia_keyword_regex).count
47
- csv << [
48
- rep.official_full,
49
- rep.url,
50
- trumpcare_mentions_count,
51
- russia_mentions_count,
52
- TrumpcareTracker.ratio(trumpcare_mentions_count, russia_mentions_count)
53
- ]
54
- puts "Scraped #{rep.official_full}'s homepage"
55
- end
56
- end
57
-
58
- File.open('trumpcare_homepage_report.csv', 'w') do |file|
59
- file.write csv_homepage_report
60
- end
61
- end
62
-
63
- def mentions_mapper(doc, regex)
64
- doc.text.split("\n").select { |string| string.downcase.match?(regex) }
65
- end
66
-
67
- desc 'Audit and tweet results'
68
- task :tweet do
69
- audit_tweets(reps) do |tracker, rep|
70
- tracker.to_tweet
71
- tracker.client.update(
72
- "#{rep.official_full}\n"\
73
- "#{rep.office_locations.map { |off| "#{off.city} - #{off.phone}" }.join("\n")}"
74
- )
75
- end
76
- end
77
-
78
- def reps
79
- democrats = PYR.reps do |r|
80
- r.democrat = true
81
- r.chamber = 'upper'
82
- end
83
-
84
- independents = PYR.reps do |r|
85
- r.independent = true
86
- r.chamber = 'upper'
87
- end
88
-
89
- democrats.objects.to_a + independents.objects.to_a
90
- end
91
-
92
- def handles
93
- @_handles ||= CSV.read('twitter_handles.csv', headers: true)
94
- end
95
-
96
- def audit_rep(i, rep, &block)
97
- puts "Sending requests to Twitter API for #{rep.official_full}"
98
- alt_screen_name = handles.detect do |handle|
99
- handle['official'].downcase.strip == rep.twitter.downcase.strip
100
- end['personal/campaign']
101
- tracker = TrumpcareTracker.new(rep.official_full, rep.twitter, alt_screen_name)
102
- tracker.audit
103
- block.call(tracker, rep) if block_given?
104
- puts "#{i + 1} down. Pausing for 45 seconds to avoid hitting API rate limit."
105
- sleep(45)
106
- rescue Twitter::Error::TooManyRequests
107
- puts 'Rate limit exceeded, waiting 2 minutes'
108
- sleep(300)
109
- audit_rep(i, rep, &block)
110
- rescue Twitter::Error => e
111
- puts e.message
112
- audit_rep(i, rep, &block)
113
- end
114
-
115
- def audit_tweets(reps, &block)
116
- reps.each_with_index do |rep, i|
117
- next if rep.twitter.nil?
118
- audit_rep(i, rep, &block)
119
- end
120
- end
17
+ require 'trumpcare_tracker/reporters'
18
+ TrumpcareTracker::Reporters.new
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+ require 'pyr'
5
+ require 'trumpcare_tracker'
6
+
7
+ class TrumpcareTracker
8
+ # Base rake task class
9
+ class RakeTask < Rake::TaskLib
10
+ module Methods
11
+ def reps
12
+ democrats = PYR.reps do |r|
13
+ r.democrat = true
14
+ r.chamber = 'upper'
15
+ end
16
+
17
+ independents = PYR.reps do |r|
18
+ r.independent = true
19
+ r.chamber = 'upper'
20
+ end
21
+
22
+ democrats.objects.to_a + independents.objects.to_a
23
+ end
24
+
25
+ def handles
26
+ @_handles ||= CSV.read('twitter_handles.csv', headers: true)
27
+ end
28
+
29
+ def audit_rep(i, rep, &block)
30
+ puts "Sending requests to Twitter API for #{rep.official_full}"
31
+ alt_screen_name = handles.detect do |handle|
32
+ handle['official'].downcase.strip == rep.twitter.downcase.strip
33
+ end['personal/campaign']
34
+ tracker = TrumpcareTracker.new(rep.official_full, rep.twitter, alt_screen_name)
35
+ tracker.audit
36
+ block.call(tracker, rep) if block_given?
37
+ puts "#{i + 1} down. Pausing for 45 seconds to avoid hitting API rate limit."
38
+ sleep(45)
39
+ rescue Twitter::Error::TooManyRequests
40
+ puts 'Rate limit exceeded, waiting 5 minutes'
41
+ sleep(300)
42
+ audit_rep(i, rep, &block)
43
+ rescue Twitter::Error => e
44
+ puts e.message
45
+ audit_rep(i, rep, &block)
46
+ end
47
+
48
+ def audit_tweets(reps, &block)
49
+ reps.each_with_index do |rep, i|
50
+ next if rep.twitter.nil?
51
+ audit_rep(i, rep, &block)
52
+ end
53
+ ensure
54
+ `say "beep beep beep beep beep"`
55
+ end
56
+
57
+ def mentions_mapper(doc, regex)
58
+ doc.text.split("\n").select { |string| string.downcase.match?(regex) }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'open-uri'
5
+ require 'trumpcare_tracker/rake_task'
6
+
7
+ class TrumpcareTracker
8
+ # Rake tasks to generate audit reports in CSV format
9
+ #
10
+ # require 'trumpcare_tracker/reporters'
11
+ # TrumpcareTracker::Reporters.new
12
+ #
13
+ # $ bundle exec rake tracker:export
14
+ # $ bundle exec rake tracker:homepage_scraper
15
+ class Reporters < RakeTask
16
+ include RakeTask::Methods
17
+
18
+ def initialize
19
+ export_task
20
+ homepage_scraper_task
21
+ end
22
+
23
+ def export_task
24
+ namespace :tracker do
25
+ desc 'Output a report of Senate Democrats Trumpcare tweets'
26
+ task :export do
27
+ csv_tweet_report = CSV.generate do |csv|
28
+ csv << %w[
29
+ senator
30
+ official_user_name
31
+ alt_user_name
32
+ tweets_in_last_seven_days
33
+ trumpcare_tweets
34
+ tct_percentage
35
+ russia_tweets
36
+ rt_percentage
37
+ tct_to_rt_ratio
38
+ trumpcare_tweet_urls
39
+ russia_tweet_urls
40
+ ]
41
+ audit_tweets(reps) { |tracker| csv << tracker.to_h.values }
42
+ end
43
+
44
+ File.open('trumpcare_tweet_report.csv', 'w') do |file|
45
+ file.write csv_tweet_report
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+
53
+ def homepage_scraper_task
54
+ namespace :tracker do
55
+ desc 'Search Senators\' official homepage for TrumpCare and Russia Mentions'
56
+ task :homepage_scraper do
57
+ csv_homepage_report = CSV.generate do |csv|
58
+ csv << %w[senator url trumpcare_mentions russia_mentions trumpcare_to_russia_ratio]
59
+ reps.each do |rep|
60
+ doc = Nokogiri::HTML(open(rep.url))
61
+ trumpcare_mentions_count = mentions_mapper(doc, TrumpcareTracker.trumpcare_keyword_regex).count
62
+ russia_mentions_count = mentions_mapper(doc, TrumpcareTracker.russia_keyword_regex).count
63
+ csv << [
64
+ rep.official_full,
65
+ rep.url,
66
+ trumpcare_mentions_count,
67
+ russia_mentions_count,
68
+ TrumpcareTracker.ratio(trumpcare_mentions_count, russia_mentions_count)
69
+ ]
70
+ puts "Scraped #{rep.official_full}'s homepage"
71
+ end
72
+ end
73
+
74
+ File.open('trumpcare_homepage_report.csv', 'w') do |file|
75
+ file.write csv_homepage_report
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'rake/tasklib'
5
+ require 'trumpcare_tracker/rake_task'
6
+
7
+ class TrumpcareTracker
8
+ # Provide a tweet bot that can be run as a rake task
9
+ #
10
+ # require 'trumpcare_tracker/tweet_bot'
11
+ # TrumpcareTracker::TweetBot.new('screen_user_name') do
12
+ # "Text for optional intro tweet to thread"
13
+ # end
14
+ #
15
+ # $ bundle exec rake tracker:tweet_bot
16
+ class TweetBot < RakeTask
17
+ include RakeTask::Methods
18
+
19
+ def initialize(screen_name, &first_tweet_block)
20
+ namespace(:tracker) do
21
+ desc 'Audit Trumpcare tweet activity and post a thread of updates'
22
+
23
+ task(:tweet_bot) do
24
+ tracker = TrumpcareTracker.new('TrumpCareTracker', screen_name)
25
+ tweet = Twitter::Tweet.new(id: nil)
26
+ first_tweet = tracker.client.update(first_tweet_block.call) if block_given?
27
+ reps.each_with_index do |rep, i|
28
+ next if rep.twitter.nil?
29
+ reply_to_tweet = if i.zero?
30
+ first_tweet
31
+ else
32
+ tweet
33
+ end
34
+
35
+ begin
36
+ tweet = post(rep, i, reply_to_tweet)
37
+ rescue Twitter::Error => e
38
+ puts e.message
39
+ puts 'Waiting 5 minutes to see if the issue can be resolved.'
40
+ sleep(300)
41
+ tweet = post(rep, i, reply_to_tweet)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def post(rep, index, reply_to_tweet)
49
+ puts "Sending requests to Twitter API for #{rep.official_full}"
50
+ tracker = TrumpcareTracker.new(rep.official_full, rep.twitter)
51
+ tweet = tracker.to_tweet(in_reply_to_status: reply_to_tweet)
52
+ sleep(rand(1..5))
53
+ contacts = "#{rep.official_full}\n"\
54
+ "#{rep.office_locations.map { |off| "#{off.city} - #{off.phone}" }.join("\n")}"
55
+ while contacts.length > 140
56
+ contacts = contacts.split("\n")[0..-2].join("\n")
57
+ end
58
+ tweet = tracker.client.update(contacts, in_reply_to_status: tweet)
59
+ puts "#{index + 1} down. Pausing for some time to avoid hitting API rate limit."
60
+ sleep(rand(30..60))
61
+ tweet
62
+ end
63
+ end
64
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TrumpcareTracker
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pyr'
4
3
  require 'trumpcare_tracker/version'
5
4
  require 'twitter'
6
5
 
@@ -23,7 +22,7 @@ class TrumpcareTracker
23
22
  end
24
23
 
25
24
  def self.trumpcare_keyword_regex
26
- /(ahca|trumpcare|healthcare|health|care|drug|medication|prescription|vaccine|obamacare)/
25
+ /(ahca|trumpcare|healthcare|health|care|drug|medication|prescription|vaccine|obamacare|cbo|premiums|insurance|deductibles)/
27
26
  end
28
27
 
29
28
  def self.russia_keyword_regex
@@ -141,7 +140,7 @@ class TrumpcareTracker
141
140
  alt_screen_name.screen_name
142
141
  end
143
142
 
144
- def to_tweet
145
- client.update(".#{self}")
143
+ def to_tweet(options = {})
144
+ client.update(".#{self}", options)
146
145
  end
147
146
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trumpcare_tracker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - M. Simon Borg
@@ -99,8 +99,10 @@ files:
99
99
  - bin/console
100
100
  - bin/setup
101
101
  - lib/trumpcare_tracker.rb
102
+ - lib/trumpcare_tracker/rake_task.rb
103
+ - lib/trumpcare_tracker/reporters.rb
104
+ - lib/trumpcare_tracker/tweet_bot.rb
102
105
  - lib/trumpcare_tracker/version.rb
103
- - trumpcare_homepage_report.csv
104
106
  - trumpcare_tracker.gemspec
105
107
  - trumpcare_tweet_report.csv
106
108
  - twitter_handles.csv
@@ -1,49 +0,0 @@
1
- senator,url,trumpcare_mentions,russia_mentions,trumpcare_to_russia_ratio
2
- Sherrod Brown,https://www.brown.senate.gov,4,1,4.0
3
- Tammy Baldwin,https://www.baldwin.senate.gov,1,0,0.0
4
- Michael F. Bennet,https://www.bennet.senate.gov,1,5,0.2
5
- Richard Blumenthal,https://www.blumenthal.senate.gov,1,0,0.0
6
- Cory A. Booker,https://www.booker.senate.gov,1,2,0.5
7
- Maria Cantwell,https://www.cantwell.senate.gov,2,0,0.0
8
- Benjamin L. Cardin,https://www.cardin.senate.gov,0,1,0.0
9
- Thomas R. Carper,https://www.carper.senate.gov/public,5,0,0.0
10
- "Robert P. Casey, Jr.",https://www.casey.senate.gov,1,2,0.5
11
- Christopher A. Coons,https://www.coons.senate.gov,2,1,2.0
12
- Catherine Cortez Masto,https://www.cortezmasto.senate.gov,0,0,0.0
13
- Richard J. Durbin,https://www.durbin.senate.gov,1,1,1.0
14
- Joe Donnelly,https://www.donnelly.senate.gov,2,1,2.0
15
- Tammy Duckworth,https://www.duckworth.senate.gov,0,1,0.0
16
- Dianne Feinstein,https://www.feinstein.senate.gov,0,0,0.0
17
- Al Franken,https://www.franken.senate.gov,8,2,4.0
18
- Kirsten E. Gillibrand,https://www.gillibrand.senate.gov,0,0,0.0
19
- Mazie K. Hirono,https://www.hirono.senate.gov,0,0,0.0
20
- Martin Heinrich,https://www.heinrich.senate.gov,4,3,1.3333
21
- Heidi Heitkamp,https://www.heitkamp.senate.gov/public,2,1,2.0
22
- Kamala D. Harris,https://www.harris.senate.gov,0,2,0.0
23
- Margaret Wood Hassan,https://www.hassan.senate.gov,0,1,0.0
24
- Amy Klobuchar,https://www.klobuchar.senate.gov,0,0,0.0
25
- Tim Kaine,https://www.kaine.senate.gov,4,0,0.0
26
- Patrick J. Leahy,https://www.leahy.senate.gov,1,4,0.25
27
- Edward J. Markey,https://www.markey.senate.gov,7,0,0.0
28
- Robert Menendez,https://www.menendez.senate.gov,1,1,1.0
29
- Patty Murray,https://www.murray.senate.gov/public,6,0,0.0
30
- Christopher Murphy,https://www.murphy.senate.gov,0,1,0.0
31
- Claire McCaskill,https://www.mccaskill.senate.gov,1,0,0.0
32
- Jeff Merkley,https://www.merkley.senate.gov,1,1,1.0
33
- "Joe Manchin, III",https://www.manchin.senate.gov/public,4,4,1.0
34
- Bill Nelson,https://www.billnelson.senate.gov,1,0,0.0
35
- Gary C. Peters,https://www.peters.senate.gov,0,0,0.0
36
- Jack Reed,https://www.reed.senate.gov,3,1,3.0
37
- Charles E. Schumer,https://www.schumer.senate.gov,0,0,0.0
38
- Debbie Stabenow,https://www.stabenow.senate.gov,1,0,0.0
39
- Jeanne Shaheen,https://www.shaheen.senate.gov,2,0,0.0
40
- Brian Schatz,https://www.schatz.senate.gov,3,1,3.0
41
- Jon Tester,https://www.tester.senate.gov,3,0,0.0
42
- Tom Udall,https://www.tomudall.senate.gov,1,0,0.0
43
- Chris Van Hollen,https://www.vanhollen.senate.gov,0,1,0.0
44
- Ron Wyden,https://www.wyden.senate.gov,7,1,7.0
45
- Sheldon Whitehouse,https://www.whitehouse.senate.gov,0,1,0.0
46
- Mark R. Warner,https://www.warner.senate.gov,0,0,0.0
47
- Elizabeth Warren,https://www.warren.senate.gov,0,5,0.0
48
- "Angus S. King, Jr.",https://www.king.senate.gov,3,0,0.0
49
- Bernard Sanders,https://www.sanders.senate.gov,0,5,0.0