Skip to content

Commit 5547d48

Browse files
authored
Merge pull request #674 from alphagov/add-petition-statistics
Add statistics about duplicate email usage
2 parents f9b4b98 + ff49e67 commit 5547d48

25 files changed

Lines changed: 448 additions & 1 deletion

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ group :test do
5858
gem 'email_spec'
5959
gem 'launchy'
6060
gem 'webmock'
61+
gem 'test_after_commit'
6162
end
6263

6364
group :production do

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ GEM
278278
sprockets (>= 3.0.0)
279279
terrapin (0.6.0)
280280
climate_control (>= 0.0.3, < 1.0)
281+
test_after_commit (1.2.2)
282+
activerecord (>= 3.2, < 5.0)
281283
textacular (3.2.2)
282284
activerecord (>= 3.0, < 5.0)
283285
thor (0.20.3)
@@ -349,6 +351,7 @@ DEPENDENCIES
349351
sass-rails (~> 5.0)
350352
shoulda-matchers
351353
slack-notifier
354+
test_after_commit
352355
textacular
353356
uglifier
354357
webmock

app/assets/stylesheets/petitions/admin/_meta.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@
1313
}
1414
}
1515
}
16+
17+
.statistics-meta {
18+
small {
19+
@include core-14;
20+
color: $grey-1;
21+
display: block;
22+
margin-bottom: em(6, 16);
23+
}
24+
}
1625
}
1726

1827
.petition-meta-state, .petition-meta-signature-count {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Admin::PetitionStatisticsController < Admin::AdminController
2+
before_action :require_sysadmin
3+
before_action :fetch_petition
4+
5+
def update
6+
UpdatePetitionStatisticsJob.perform_later(@petition)
7+
redirect_to admin_petition_url(@petition), notice: :enqueued_petition_statistics_update
8+
end
9+
10+
private
11+
12+
def fetch_petition
13+
@petition = Petition.moderated.find(params[:petition_id])
14+
end
15+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class EnqueuePetitionStatisticsUpdatesJob < ApplicationJob
2+
queue_as :low_priority
3+
4+
def perform(timestamp)
5+
Petition.signed_since(timestamp.in_time_zone).find_each do |petition|
6+
UpdatePetitionStatisticsJob.perform_later(petition)
7+
end
8+
end
9+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class UpdatePetitionStatisticsJob < ApplicationJob
2+
queue_as :low_priority
3+
4+
def perform(petition)
5+
petition.statistics.refresh!
6+
end
7+
end

app/models/petition.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class Petition < ActiveRecord::Base
7676
has_one :government_response, dependent: :destroy
7777
has_one :note, dependent: :destroy
7878
has_one :rejection, dependent: :destroy
79+
has_one :statistics, dependent: :destroy
7980

8081
has_many :signatures
8182
has_many :sponsors, -> { sponsors }, class_name: 'Signature'
@@ -383,6 +384,10 @@ def untagged_in_moderation
383384
untagged.in_moderation
384385
end
385386

387+
def signed_since(timestamp)
388+
where(arel_table[:last_signed_at].gt(timestamp))
389+
end
390+
386391
private
387392

388393
def moderation_threshold_reached_at
@@ -429,6 +434,10 @@ def scheduled_debate_state
429434
end
430435
end
431436

437+
def statistics
438+
super || create_statistics!
439+
end
440+
432441
def update_signature_count!
433442
sql = "signature_count = (?), updated_at = ?"
434443
count = Signature.arel_table[Arel.star].count

app/models/petition/statistics.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class Petition < ActiveRecord::Base
2+
class Statistics < ActiveRecord::Base
3+
belongs_to :petition
4+
5+
after_commit on: :create do
6+
UpdatePetitionStatisticsJob.perform_later(petition)
7+
end
8+
9+
def refresh!(now = Time.current)
10+
update!(
11+
refreshed_at: now,
12+
duplicate_emails: refresh_duplicate_emails
13+
)
14+
end
15+
16+
def refreshed?
17+
refreshed_at?
18+
end
19+
20+
private
21+
22+
def refresh_duplicate_emails
23+
petition.signatures.duplicate_emails
24+
end
25+
end
26+
end

app/models/signature.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ def duplicate(id, email)
9090
where(arel_table[:id].not_eq(id).and(arel_table[:email].eq(email)))
9191
end
9292

93+
def duplicate_emails
94+
unscoped.from(validated.select(:uuid).group(:uuid).having(arel_table[Arel.star].count.gt(1))).count
95+
end
96+
9397
def for_email(email)
9498
where(email: email.downcase)
9599
end

app/views/admin/petitions/_petition_details.html.erb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
<% end %>
3838

3939
<% if current_user.is_a_sysadmin? %>
40+
<% if @petition.statistics.refreshed? %>
41+
<dt>Duplicate emails</dt>
42+
<dd><%= number_with_delimiter(@petition.statistics.duplicate_emails) %></dd>
43+
<% end %>
44+
4045
<% if @petition.fraudulent_domains? %>
4146
<dt>Fraudulent domains</dt>
4247
<dd>
@@ -53,3 +58,17 @@
5358
<% end %>
5459
<% end %>
5560
</dl>
61+
62+
<% if current_user.is_a_sysadmin? %>
63+
<div class="statistics-meta">
64+
<small>
65+
<% if @petition.statistics.refreshed? %>
66+
Statistics last updated at <time datetime="<%= @petition.statistics.refreshed_at.iso8601 %>"><%= date_time_format(@petition.statistics.refreshed_at) %></time>
67+
<% else %>
68+
Statistics not yet generated
69+
<% end %>
70+
</small>
71+
72+
<%= button_to 'Refresh', admin_petition_statistics_url(@petition), method: :patch, class: 'button' %>
73+
</div>
74+
<% end %>

0 commit comments

Comments
 (0)