require 'textacular/searchable'
require_dependency 'archived'
module Archived
class Petition < ActiveRecord::Base - Archived::Petition assumes too much for instance variable '@_signatures_by_constituency'
- Archived::Petition assumes too much for instance variable '@_signatures_by_country'
- Archived::Petition has no descriptive comment
- Archived::Petition has 9 constants
- Archived::Petition has at least 62 methods
STOPPED_STATE = 'stopped'
CLOSED_STATE = 'closed'
HIDDEN_STATE = 'hidden'
REJECTED_STATE = 'rejected'
STATES = [STOPPED_STATE, CLOSED_STATE, HIDDEN_STATE, REJECTED_STATE]
PUBLISHED_STATES = [CLOSED_STATE]
VISIBLE_STATES = [CLOSED_STATE, REJECTED_STATE]
MODERATED_STATES = [CLOSED_STATE, HIDDEN_STATE, REJECTED_STATE]
DEBATABLE_STATES = [CLOSED_STATE]
belongs_to :parliament, inverse_of: :petitions, required: true
belongs_to :locked_by, class_name: 'AdminUser'
has_one :creator, -> { creator }, class_name: "Signature"
has_one :debate_outcome, dependent: :destroy
has_one :government_response, dependent: :destroy
has_one :note, dependent: :destroy
has_one :rejection, dependent: :destroy
has_many :emails, :dependent => :destroy
has_many :signatures
has_many :sponsors, -> { sponsors }, class_name: "Signature"
validates :action, presence: true, length: { maximum: 150 }
validates :background, length: { maximum: 300 }, allow_blank: true
validates :additional_details, length: { maximum: 1000 }, allow_blank: true
validates :state, presence: true, inclusion: STATES
validates :closed_at, presence: true, if: :closed?
before_save :update_debate_state, if: :scheduled_debate_date_changed?
extend Searchable(:action, :background, :additional_details)
include Browseable, Taggable
filter :parliament
facet :all, -> { visible.by_most_signatures }
facet :awaiting_response, -> { awaiting_response.by_waiting_for_response_longest }
facet :awaiting_debate_date, -> { awaiting_debate_date.by_waiting_for_debate_longest }
facet :with_debate_outcome, -> { with_debate_outcome.by_most_recent_debate_outcome }
facet :with_debated_outcome, -> { with_debated_outcome.by_most_recent_debate_outcome }
facet :published, -> { published.by_most_signatures }
facet :stopped, -> { stopped.by_most_signatures }
facet :closed, -> { closed.by_most_signatures }
facet :rejected, -> { rejected.by_most_signatures }
facet :hidden, -> { hidden.by_most_recent }
facet :with_response, -> { with_response.by_most_signatures }
facet :debated, -> { debated.by_most_recent_debate_outcome }
facet :not_debated, -> { not_debated.by_most_recent_debate_outcome }
facet :by_most_signatures, -> { by_most_signatures }
facet :by_created_at, -> { by_created_at }
facet :in_debate_queue, -> { in_debate_queue.by_waiting_for_debate_longest }
default_scope { preload(:parliament) }
delegate :threshold_for_response, :threshold_for_debate, to: :parliament
delegate :show_on_a_map?, to: :parliament
with_options allow_nil: true, prefix: true do -
delegate :name, :email, to: :creator
delegate :code, :details, to: :rejection
delegate :summary, :details, :created_at, :updated_at, to: :government_response
delegate :date, :transcript_url, :video_url, :overview, to: :debate_outcome, prefix: :debate
delegate :debate_pack_url, to: :debate_outcome, prefix: false
end
alias_attribute :open_at, :opened_at
class << self
def for_state(state)
where(state: state)
end
def by_created_at
reorder(created_at: :asc)
end
def by_most_recent_debate_outcome
reorder(debate_outcome_at: :desc, created_at: :desc)
end
def by_waiting_for_debate_longest
reorder(debate_threshold_reached_at: :asc, created_at: :desc)
end
def by_most_recent
reorder(created_at: :desc)
end
def by_most_signatures
reorder(signature_count: :desc)
end
def by_waiting_for_response_longest
reorder(response_threshold_reached_at: :asc, created_at: :desc)
end
def awaiting_debate_date
debate_threshold_reached.not_scheduled
end
def awaiting_response
response_threshold_reached.not_responded
end
def not_responded
where(government_response_at: nil)
end
def with_response
where.not(government_response_at: nil).preload(:government_response)
end
def response_threshold_reached
where.not(response_threshold_reached_at: nil)
end
def published
where(state: PUBLISHED_STATES)
end
def moderated
where(state: MODERATED_STATES)
end
def stopped
where(state: STOPPED_STATE)
end
def closed
where(state: CLOSED_STATE)
end
def rejected
where(state: REJECTED_STATE)
end
def hidden
where(state: HIDDEN_STATE)
end
def debateable
where(state: DEBATABLE_STATES)
end
def debated
where(debate_state: 'debated').preload(:debate_outcome)
end
def not_debated
where(debate_state: 'not_debated')
end
def debate_threshold_reached
where.not(debate_threshold_reached_at: nil)
end
def debate_scheduled
where.not(scheduled_debate_date: nil)
end
def not_scheduled
where(scheduled_debate_date: nil)
end
def with_debate_outcome
where.not(debate_outcome_at: nil)
end
def with_debated_outcome
debated.where.not(debate_outcome_at: nil)
end
def visible
where(state: VISIBLE_STATES)
end
def in_need_of_marking_as_debated(date = Date.current)
where(scheduled_debate_state.and(debate_date_in_the_past(date)))
end
def mark_petitions_as_debated!(date = Date.current) - Archived::Petition has missing safe method 'mark_petitions_as_debated!'
in_need_of_marking_as_debated(date).update_all(debate_state: 'debated')
end
def in_debate_queue
where(threshold_for_debate_reached.or(scheduled_for_debate))
end
private
def debate_date_in_the_past(date)
arel_table[:scheduled_debate_date].lt(date)
end
def scheduled_debate_state
arel_table[:debate_state].eq('scheduled')
end
def threshold_for_debate_reached
arel_table[:debate_threshold_reached_at].not_eq(nil)
end
def scheduled_for_debate
arel_table[:scheduled_debate_date].not_eq(nil)
end
end
def moderated?
state.in?(MODERATED_STATES)
end
def can_have_debate_added?
state.in?(DEBATABLE_STATES)
end
def stopped?
state == STOPPED_STATE
end
def closed?
state == CLOSED_STATE
end
def rejected?
state == REJECTED_STATE
end
def hidden?
state == HIDDEN_STATE
end
def published?
state.in?(PUBLISHED_STATES)
end
def duration
if parliament.petition_duration?
parliament.petition_duration
elsif opened_at?
calculate_petition_duration
else
0
end
end
def closed_early_due_to_election?
closed_at == parliament.dissolution_at
end
def government_response?
government_response_at && government_response
end
def threshold_for_debate_reached?
signature_count >= parliament.threshold_for_debate
end
def threshold_for_response_reached?
signature_count >= parliament.threshold_for_response
end
def signatures_by_constituency
if defined?(@_signatures_by_constituency)
@_signatures_by_constituency
else
if signatures_by_constituency?
@_signatures_by_constituency = calculate_signatures_by_constituency(super)
else
[]
end
end
end
def signatures_by_country
if defined?(@_signatures_by_country)
@_signatures_by_country
else
if signatures_by_country?
@_signatures_by_country = calculate_signatures_by_country(super)
else
[]
end
end
end
def get_email_requested_at_for(name)
self["email_requested_for_#{name}_at"]
end
def set_email_requested_at_for(name, to: Time.current)
update_column("email_requested_for_#{name}_at", to)
end
def signatures_to_email_for(name)
if timestamp = get_email_requested_at_for(name)
signatures.need_emailing_for(name, since: timestamp)
else
raise ArgumentError, "The #{name} email has not been requested for petition #{id}"
end
end
def update_debate_state
self.debate_state = evaluate_debate_state
end
def update_lock!(user, now = Time.current) -
- Archived::Petition has missing safe method 'update_lock!'
if locked_by == user - Archived::Petition#update_lock! is controlled by argument 'user'
update!(locked_at: now)
end
end
def checkout!(user, now = Time.current) -
-
- Archived::Petition has missing safe method 'checkout!'
with_lock do
if locked_by.present? && locked_by != user
false
else
update!(locked_by: user, locked_at: now)
end
end
end
def force_checkout!(user, now = Time.current) -
- Archived::Petition has missing safe method 'force_checkout!'
update!(locked_by: user, locked_at: now)
end
def release!(user) -
- Archived::Petition has missing safe method 'release!'
with_lock do
if locked_by.present? && locked_by == user - Archived::Petition#release! is controlled by argument 'user'
update!(locked_by: nil, locked_at: nil)
end
end
end
private
def evaluate_debate_state
if scheduled_debate_date?
scheduled_debate_date > Date.current ? 'scheduled' : 'debated'
else
'awaiting'
end
end
def calculate_petition_duration - Archived::Petition#calculate_petition_duration has a flog score of 46
if opened_at + 3.months == closed_at
3
elsif opened_at + 6.months == closed_at
6
elsif opened_at + 9.months == closed_at
9
elsif opened_at + 12.months == closed_at
12
else
Rational(closed_at - opened_at, 86400 * 30).to_f
end
end
def constituencies(external_ids) - Archived::Petition#constituencies doesn't depend on instance state (maybe move it to another class?)
Constituency.where(external_id: external_ids).order(:name)
end
def calculate_signatures_by_constituency(hash)
constituencies(hash.keys).map do |constituency|
{
name: constituency.name, -
ons_code: constituency.ons_code, -
mp: constituency.mp_name, -
signature_count: hash[constituency.external_id] -
}
end
end
def locations(codes) - Archived::Petition#locations doesn't depend on instance state (maybe move it to another class?)
Location.where(code: codes).order(:name)
end
def calculate_signatures_by_country(hash)
locations(hash.keys).map do |location|
{
name: location.name, -
code: location.code, -
-
signature_count: hash[location.code]
}
end
end
end
end