Class: Proposal

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/proposal.rb

Direct Known Subclasses

ConstitutionProposal, FoundOrganisationProposal, MembershipProposal

Class Method Summary (collapse)

Instance Method Summary (collapse)

Class Method Details

+ (Object) close_due_proposals



179
180
181
# File 'app/models/proposal.rb', line 179

def self.close_due_proposals
  where(["close_date < ? AND open = 1", Time.now.utc]).all.each { |p| p.close! }
end

+ (Object) close_early_proposals



183
184
185
# File 'app/models/proposal.rb', line 183

def self.close_early_proposals
  find_closeable_early_proposals.each { |p| p.close! }
end

+ (Object) close_proposals

Called every 60 seconds in the worker process (set up at end of file)



188
189
190
191
# File 'app/models/proposal.rb', line 188

def self.close_proposals
  close_due_proposals
  close_early_proposals
end

+ (Object) find_closeable_early_proposals



175
176
177
# File 'app/models/proposal.rb', line 175

def self.find_closeable_early_proposals
  currently_open.all.select { |p| p.voting_system.can_be_closed_early?(p) }
end

+ (Object) send_email_for(proposal_id)

only to be backwards compatible with systems running older versions of delayed job



205
206
207
# File 'app/models/proposal.rb', line 205

def self.send_email_for(proposal_id)
  Proposal.find(proposal_id).send_email_without_send_later
end

Instance Method Details

- (Object) abstained



105
106
107
# File 'app/models/proposal.rb', line 105

def abstained
  member_count - total_votes
end

- (Object) accepted_or_rejected



117
118
119
# File 'app/models/proposal.rb', line 117

def accepted_or_rejected
  accepted? ? "accepted" : "rejected"
end

- (Boolean) allows_direct_edit?

Returns:

  • (Boolean)


29
30
31
# File 'app/models/proposal.rb', line 29

def allows_direct_edit?
  false
end

- (Boolean) automatic_proposer_support_vote?

Should the proposal automatically create a support vote by the proposer when the proposal is started?

Returns:

  • (Boolean)


39
40
41
# File 'app/models/proposal.rb', line 39

def automatic_proposer_support_vote?
  true
end

- (Object) close!



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'app/models/proposal.rb', line 143

def close!
  raise "proposal #{self} already closed" if closed?   
      
  passed = passed?
  Rails.logger.info("closing proposal #{self}")
      
  self.open = 0
  self.close_date = Time.now
  self.accepted = passed
  save!
  
  if passed
    enact!(self.parameters)
    decision = self.create_decision
    begin
      decision.send_email
    rescue => e
      Rails.logger.error("Error sending decision email: #{e.inspect}")
    end
  else
    reject!(self.parameters)
  end
end

- (Boolean) closed?

Returns:

  • (Boolean)


124
125
126
# File 'app/models/proposal.rb', line 124

def closed?
  ! self.open?
end

- (Object) decision_notification_message



217
218
219
# File 'app/models/proposal.rb', line 217

def decision_notification_message
  nil
end

- (Object) duration



213
214
215
# File 'app/models/proposal.rb', line 213

def duration
  creation_date && end_date && (end_date - creation_date)
end

- (Object) enact!(params = {})



121
122
# File 'app/models/proposal.rb', line 121

def enact!(params={})
end

- (Object) end_date



60
61
62
# File 'app/models/proposal.rb', line 60

def end_date
  self.close_date
end

- (Object) member_count

returns the number of members who are eligible to vote on this proposal



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'app/models/proposal.rb', line 78

def member_count
  # TODO: find out how to do the following in one query
  count = 0
  
  # To vote, a member must be inducted, and must have been added (created)
  # before this proposal was made.
  
  # Members who were founding members are an exception. They are allowed
  # to vote in proposals before they have been inducted.
  # (This is because, by participating in the founding vote, they have
  # already agreed to the constitution.)
  # We determine who was a founding member by seeing whether they were
  # created before the FoundOrganisationProposal for this org.
  
  fop = organisation.found_organisation_proposals.last
  if fop
    organisation.members.where(["active = 1 AND ((created_at < ? AND inducted_at IS NOT NULL) OR (created_at < ?))", creation_date, fop.creation_date]).each do |m|
      count += 1 if m.has_permission(:vote)
    end
  else
    organisation.members.where(["(created_at < ? AND active = 1 AND inducted_at IS NOT NULL)", creation_date]).each do |m|
      count += 1 if m.has_permission(:vote)
    end
  end
  count
end

- (Object) parameters



167
168
169
# File 'app/models/proposal.rb', line 167

def parameters
  self[:parameters].blank? ? {} : ActiveSupport::JSON.decode(self[:parameters])
end

- (Object) parameters=(new_parameters)



171
172
173
# File 'app/models/proposal.rb', line 171

def parameters=(new_parameters)
  self[:parameters] = new_parameters.to_json
end

- (Boolean) passed?

Returns:

  • (Boolean)


139
140
141
# File 'app/models/proposal.rb', line 139

def passed?
  @force_passed || voting_system.passed?(self)
end

- (Object) reject!(params = {})



113
114
115
# File 'app/models/proposal.rb', line 113

def reject!(params={})
  # TODO do some kind of email notification
end

- (Object) send_email



197
198
199
200
201
202
# File 'app/models/proposal.rb', line 197

def send_email
  self.organisation.members.active.each do |m|
    # only notify members who can vote
    ProposalMailer.notify_creation(m, self).deliver if m.has_permission(:vote)
  end
end

- (Object) start

Call this to kick off a proposal. If the organisation is pending this will simply enact the proposal. If the organisation is "live" then a proposal will get created. Returns true on success, false otherwise.



47
48
49
50
51
52
53
54
55
56
57
58
# File 'app/models/proposal.rb', line 47

def start
  if organisation.pending? and allows_direct_edit? and proposer.has_permission(:direct_edit)
    self.accepted = true
    self.force_pass!
    self.enact!(self.parameters)
    true
  else
    save_succeeded = save
    proposer.cast_vote(:for, self.id) if save_succeeded && automatic_proposer_support_vote?
    save_succeeded
  end
end

- (Object) to_event



209
210
211
# File 'app/models/proposal.rb', line 209

def to_event
  {:timestamp => self.creation_date, :object => self, :kind => (closed? && !accepted?) ? :failed_proposal : :proposal }
end

- (Object) total_votes



109
110
111
# File 'app/models/proposal.rb', line 109

def total_votes
  votes_for + votes_against
end

- (Object) vote_by(member)

Returns a Vote by the member specified, or Nil



65
66
67
# File 'app/models/proposal.rb', line 65

def vote_by(member)
  member.votes.where(:proposal_id => self.id).first
end

- (Object) votes_against



73
74
75
# File 'app/models/proposal.rb', line 73

def votes_against
  Vote.where(:proposal_id => self.id, :for => false).count
end

- (Object) votes_for



69
70
71
# File 'app/models/proposal.rb', line 69

def votes_for
  Vote.where(:proposal_id => self.id, :for => true).count
end

- (Object) voting_period



33
34
35
# File 'app/models/proposal.rb', line 33

def voting_period
  organisation.constitution.voting_period
end

- (Object) voting_system



128
129
130
# File 'app/models/proposal.rb', line 128

def voting_system
  organisation.constitution.voting_system(:general)
end