Skip to content

Commit 4c4b2cd

Browse files
authored
Merge pull request #596 from alphagov/add-additional-columns-for-archived-petitions
Add migration task for legacy data
2 parents 28ba0ad + fd34761 commit 4c4b2cd

21 files changed

Lines changed: 1759 additions & 248 deletions
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
module Archived
2+
class DebateOutcome < ActiveRecord::Base
3+
# By default we want the user to upload a '2x' style image, and we can then
4+
# resize it down with Imagemagick
5+
COMMONS_IMAGE_SIZE = { w: 1260.0, h: 710.0 }
6+
7+
belongs_to :petition, touch: true
8+
9+
validates :petition, presence: true
10+
validates :debated_on, presence: true, if: :debated?
11+
validates :transcript_url, :video_url, length: { maximum: 500 }
12+
13+
has_attached_file :commons_image,
14+
# default_url needs to be a lambda - this way the generated image url will
15+
# include any asset-digest
16+
default_url: ->(_) { ActionController::Base.helpers.image_url("graphics/graphic_house-of-commons.jpg") },
17+
styles: {
18+
"1x": "#{(COMMONS_IMAGE_SIZE[:w]/2).to_i}x#{(COMMONS_IMAGE_SIZE[:h]/2).to_i}",
19+
"2x": "#{COMMONS_IMAGE_SIZE[:w]}x#{COMMONS_IMAGE_SIZE[:h]}"
20+
}
21+
22+
validates_attachment_content_type :commons_image, content_type: /\Aimage\/.*\Z/
23+
validate :validate_commons_image_dimensions, unless: :no_commons_image_queued
24+
25+
after_create do
26+
petition.touch(:debate_outcome_at)
27+
end
28+
29+
after_save do
30+
petition.update_columns(debate_state: debate_state)
31+
end
32+
33+
def date
34+
debated_on
35+
end
36+
37+
private
38+
39+
def debate_state
40+
debated? ? 'debated' : 'not_debated'
41+
end
42+
43+
def image_ratio(width, height)
44+
(width.to_f / height.to_f).round(2)
45+
end
46+
47+
def no_commons_image_queued
48+
commons_image.blank? || !commons_image.queued_for_write[:original]
49+
end
50+
51+
def validate_commons_image_dimensions
52+
# This should be tuned if the images start looking badly scaled
53+
max_ratio_delta = 0.1
54+
55+
dimensions = Paperclip::Geometry.from_file(commons_image.queued_for_write[:original].path)
56+
57+
if dimensions.width < COMMONS_IMAGE_SIZE[:w]
58+
errors.add(:commons_image, :too_narrow, width: dimensions.width, min_width: COMMONS_IMAGE_SIZE[:w])
59+
end
60+
61+
if dimensions.height < COMMONS_IMAGE_SIZE[:h]
62+
errors.add(:commons_image, :too_short, height: dimensions.height, min_height: COMMONS_IMAGE_SIZE[:h])
63+
end
64+
65+
expected_ratio = image_ratio(COMMONS_IMAGE_SIZE[:w], COMMONS_IMAGE_SIZE[:h])
66+
actual_ratio = image_ratio(dimensions.width, dimensions.height)
67+
68+
min_ratio = (expected_ratio - max_ratio_delta).round(2)
69+
max_ratio = (expected_ratio + max_ratio_delta).round(2)
70+
unless (min_ratio..max_ratio).include? actual_ratio
71+
errors.add(:commons_image, :incorrect_ratio, ratio: actual_ratio, min_ratio: min_ratio, max_ratio: max_ratio)
72+
end
73+
end
74+
end
75+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Archived
2+
class GovernmentResponse < ActiveRecord::Base
3+
belongs_to :petition, touch: true
4+
5+
validates :petition, presence: true
6+
validates :summary, length: { maximum: 200 }, allow_blank: true
7+
validates :details, length: { maximum: 10000 }, allow_blank: true
8+
9+
after_create do
10+
petition.touch(:government_response_at)
11+
end
12+
end
13+
end

app/models/archived/note.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Archived
2+
class Note < ActiveRecord::Base
3+
belongs_to :petition, touch: true
4+
5+
validates :petition, presence: true
6+
end
7+
end

app/models/archived/petition.rb

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,23 @@ module Archived
44
class Petition < ActiveRecord::Base
55
OPEN_STATE = 'open'
66
CLOSED_STATE = 'closed'
7+
HIDDEN_STATE = 'hidden'
78
REJECTED_STATE = 'rejected'
8-
STATES = [OPEN_STATE, CLOSED_STATE, REJECTED_STATE]
9+
STATES = [OPEN_STATE, CLOSED_STATE, HIDDEN_STATE, REJECTED_STATE]
910
PUBLISHED_STATES = [OPEN_STATE, CLOSED_STATE]
1011

11-
alias_attribute :action, :title
12-
1312
belongs_to :parliament, inverse_of: :petitions, required: true
1413

14+
has_one :creator, -> { where(creator: true) }, class_name: "Signature"
15+
has_one :debate_outcome, dependent: :destroy
16+
has_one :government_response, dependent: :destroy
17+
has_one :note, dependent: :destroy
18+
has_one :rejection, dependent: :destroy
19+
20+
has_many :emails, :dependent => :destroy
21+
has_many :signatures
22+
has_many :sponsors, -> { where(sponsor: true) }, class_name: "Signature"
23+
1524
validates :title, presence: true, length: { maximum: 150 }
1625
validates :description, presence: true, length: { maximum: 1000 }
1726
validates :state, presence: true, inclusion: STATES
@@ -48,6 +57,14 @@ def by_most_signatures
4857
end
4958
end
5059

60+
def action
61+
super || title
62+
end
63+
64+
def action?
65+
super || title?
66+
end
67+
5168
def open?
5269
state == OPEN_STATE
5370
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module Archived
2+
class Petition < ActiveRecord::Base
3+
class Email < ActiveRecord::Base
4+
belongs_to :petition, touch: true
5+
6+
validates :petition, presence: true
7+
validates :subject, presence: true, length: { maximum: 100 }
8+
validates :body, presence: true, length: { maximum: 5000 }
9+
end
10+
end
11+
end

app/models/archived/rejection.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module Archived
2+
class Rejection < ActiveRecord::Base
3+
CODES = %w[duplicate irrelevant no-action honours fake-name foi libellous offensive]
4+
HIDDEN_CODES = %w[libellous offensive]
5+
6+
belongs_to :petition, touch: true
7+
8+
validates :petition, presence: true
9+
validates :code, presence: true, inclusion: { in: CODES }
10+
validates :details, length: { maximum: 4000 }, allow_blank: true
11+
12+
after_create do
13+
petition.update!(state: state_for_petition, rejected_at: Time.current)
14+
end
15+
16+
def hide_petition?
17+
code.in?(HIDDEN_CODES)
18+
end
19+
20+
def state_for_petition
21+
hide_petition? ? Petition::HIDDEN_STATE : Petition::REJECTED_STATE
22+
end
23+
end
24+
end

app/models/archived/signature.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
module Archived
2+
class Signature < ActiveRecord::Base
3+
PENDING_STATE = 'pending'
4+
FRAUDULENT_STATE = 'fraudulent'
5+
VALIDATED_STATE = 'validated'
6+
INVALIDATED_STATE = 'invalidated'
7+
8+
STATES = [
9+
PENDING_STATE, FRAUDULENT_STATE,
10+
VALIDATED_STATE, INVALIDATED_STATE
11+
]
12+
13+
TIMESTAMPS = {
14+
'government_response' => :government_response_email_at,
15+
'debate_scheduled' => :debate_scheduled_email_at,
16+
'debate_outcome' => :debate_outcome_email_at,
17+
'petition_email' => :petition_email_at
18+
}
19+
20+
belongs_to :petition
21+
belongs_to :invalidation
22+
belongs_to :constituency, primary_key: :external_id
23+
24+
validates :constituency_id, length: { maximum: 255 }
25+
validates :email, presence: true
26+
validates :location_code, presence: true
27+
validates :name, presence: true, length: { maximum: 255 }
28+
validates :state, presence: true, inclusion: { in: STATES }
29+
30+
def self.batch(id = 0, limit: 1000)
31+
where(arel_table[:id].gteq(id)).order(id: :asc).limit(limit)
32+
end
33+
34+
def pending?
35+
state == PENDING_STATE
36+
end
37+
38+
def fraudulent?
39+
state == FRAUDULENT_STATE
40+
end
41+
42+
def validated?
43+
state == VALIDATED_STATE
44+
end
45+
46+
def invalidated?
47+
state == INVALIDATED_STATE
48+
end
49+
50+
def unsubscribed?
51+
notify_by_email == false
52+
end
53+
54+
def unsubscribe!(token)
55+
if unsubscribed?
56+
errors.add(:base, "Already Unsubscribed")
57+
elsif unsubscribe_token != token
58+
errors.add(:base, "Invalid Unsubscribe Token")
59+
else
60+
update(notify_by_email: false)
61+
end
62+
end
63+
64+
def already_unsubscribed?
65+
errors[:base].include?("Already Unsubscribed")
66+
end
67+
68+
def invalid_unsubscribe_token?
69+
errors[:base].include?("Invalid Unsubscribe Token")
70+
end
71+
end
72+
end

config/locales/activerecord.en-GB.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ en-GB:
2424
role:
2525
inclusion: "Role '%{value}' is invalid"
2626

27+
archived/debate_outcome:
28+
attributes:
29+
commons_image:
30+
too_narrow: "Width must be at least %{min_width}px (is %{width}px)"
31+
too_short: "Height must be at least %{min_height}px (is %{height}px)"
32+
incorrect_ratio: "Width and height ratio of uploaded image is %{ratio} - should be between %{min_ratio} and %{max_ratio}"
33+
2734
petition:
2835
attributes:
2936
moderation:
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
class ReplicatePetitionModelsForArchive < ActiveRecord::Migration
2+
def change
3+
create_table :archived_debate_outcomes do |t|
4+
t.integer :petition_id, null: false
5+
t.date :debated_on
6+
t.string :transcript_url, limit: 500
7+
t.string :video_url, limit: 500
8+
t.text :overview
9+
t.datetime :created_at, null: false
10+
t.datetime :updated_at, null: false
11+
t.boolean :debated, default: true, null: false
12+
t.string :commons_image_file_name
13+
t.string :commons_image_content_type
14+
t.integer :commons_image_file_size
15+
t.datetime :commons_image_updated_at
16+
end
17+
18+
add_index :archived_debate_outcomes, [:petition_id, :debated_on]
19+
add_index :archived_debate_outcomes, :petition_id, unique: true
20+
add_index :archived_debate_outcomes, :updated_at
21+
add_foreign_key :archived_debate_outcomes, :archived_petitions, column: :petition_id, on_delete: :cascade
22+
23+
create_table :archived_government_responses do |t|
24+
t.integer :petition_id
25+
t.string :summary, limit: 500, null: false
26+
t.text :details
27+
t.datetime :created_at, null: false
28+
t.datetime :updated_at, null: false
29+
end
30+
31+
add_index :archived_government_responses, :petition_id, unique: true
32+
add_index :archived_government_responses, :updated_at
33+
add_foreign_key :archived_government_responses, :archived_petitions, column: :petition_id, on_delete: :cascade
34+
35+
create_table :archived_notes do |t|
36+
t.integer :petition_id
37+
t.text :details
38+
t.datetime :created_at, null: false
39+
t.datetime :updated_at, null: false
40+
end
41+
42+
add_index :archived_notes, :petition_id, unique: true
43+
add_foreign_key :archived_notes, :archived_petitions, column: :petition_id, on_delete: :cascade
44+
45+
create_table :archived_petition_emails, force: :cascade do |t|
46+
t.integer :petition_id
47+
t.string :subject, null: false
48+
t.text :body
49+
t.string :sent_by
50+
t.datetime :created_at, null: false
51+
t.datetime :updated_at, null: false
52+
end
53+
54+
add_index :archived_petition_emails, :petition_id
55+
add_foreign_key :archived_petition_emails, :archived_petitions, column: :petition_id, on_delete: :cascade
56+
57+
create_table :archived_rejections, force: :cascade do |t|
58+
t.integer :petition_id
59+
t.string :code, limit: 50, null: false
60+
t.text :details
61+
t.datetime :created_at, null: false
62+
t.datetime :updated_at, null: false
63+
end
64+
65+
add_index :archived_rejections, :petition_id, unique: true
66+
add_foreign_key :archived_rejections, :archived_petitions, column: :petition_id, on_delete: :cascade
67+
68+
create_table :archived_signatures do |t|
69+
t.string :name, limit: 255, null: false
70+
t.string :state, limit: 20, default: "pending", null: false
71+
t.string :perishable_token, limit: 255
72+
t.string :postcode, limit: 255
73+
t.string :ip_address, limit: 20
74+
t.integer :petition_id
75+
t.datetime :created_at
76+
t.datetime :updated_at
77+
t.boolean :notify_by_email, default: true
78+
t.string :email, limit: 255
79+
t.string :unsubscribe_token
80+
t.string :constituency_id
81+
t.datetime :validated_at
82+
t.integer :number
83+
t.boolean :seen_signed_confirmation_page, default: false, null: false
84+
t.string :location_code, limit: 30
85+
t.datetime :invalidated_at
86+
t.integer :invalidation_id
87+
t.datetime :government_response_email_at
88+
t.datetime :debate_scheduled_email_at
89+
t.datetime :debate_outcome_email_at
90+
t.datetime :petition_email_at
91+
t.uuid :uuid
92+
t.boolean :creator, default: false, null: false
93+
t.boolean :sponsor, default: false, null: false
94+
end
95+
96+
add_index :archived_signatures, :constituency_id
97+
add_index :archived_signatures, [:created_at, :ip_address, :petition_id], name: "index_archived_signatures_on_creation_ip_and_petition_id"
98+
add_index :archived_signatures, [:email, :petition_id, :name], unique: true
99+
add_index :archived_signatures, :invalidation_id
100+
add_index :archived_signatures, [:ip_address, :petition_id]
101+
add_index :archived_signatures, [:petition_id, :location_code]
102+
add_index :archived_signatures, :petition_id
103+
add_index :archived_signatures, [:state, :petition_id]
104+
add_index :archived_signatures, :updated_at
105+
add_index :archived_signatures, :uuid
106+
add_index :archived_signatures, :validated_at
107+
add_index :archived_signatures, [:creator, :petition_id]
108+
add_index :archived_signatures, [:sponsor, :petition_id]
109+
add_foreign_key :archived_signatures, :archived_petitions, column: :petition_id, on_delete: :cascade
110+
111+
add_column :archived_petitions, :action, :string, limit: 255
112+
add_column :archived_petitions, :background, :string, limit: 300
113+
add_column :archived_petitions, :additional_details, :text
114+
add_column :archived_petitions, :government_response_at, :datetime
115+
add_column :archived_petitions, :scheduled_debate_date, :date
116+
add_column :archived_petitions, :last_signed_at, :datetime
117+
add_column :archived_petitions, :response_threshold_reached_at, :datetime
118+
add_column :archived_petitions, :debate_threshold_reached_at, :datetime
119+
add_column :archived_petitions, :rejected_at, :datetime
120+
add_column :archived_petitions, :debate_outcome_at, :datetime
121+
add_column :archived_petitions, :moderation_threshold_reached_at, :datetime
122+
add_column :archived_petitions, :debate_state, :string, limit: 30
123+
add_column :archived_petitions, :stopped_at, :datetime
124+
add_column :archived_petitions, :special_consideration, :boolean
125+
end
126+
end

0 commit comments

Comments
 (0)