www/project/icla/views/actions/update.json.rb (162 lines of code) (raw):
#
# Common methods to update the progress file
#
# Called from discuss.js.rb and vote.js.rb using POST
#
# Expects the following variables to be provided as part of post data:
# - action
# - comment (possibly absent)
# - expectedPhase
# - member
# - newPhase (possibly absent)
# - token
# - vote (possibly absent)
# Returns the following hash keys:
# body_text (debug)
# rewrite (debug)
# error
# backtrace
# contents
$LOAD_PATH.unshift '/srv/whimsy/lib'
require 'json'
require 'whimsy/lockfile'
# TODO add some kind of history to show who changed the phase and when
# This probably needs to be held separately from comments
# Simplify validation
VALID_ACTIONS=%w{submitVote cancelVote tallyVote submitComment startVoting invite}
HAS_COMMENT=%w{submitComment startVoting invite} # do we update the comments array?
VALID_PHASES=%w{discuss vote cancelled tallied invite}
VALID_VOTES=%w{+1 +0 -0 -1}
# Tally the votes and send them
def sendTally(contents)
user_email = "#{@member}@apache.org"
pmc = ASF::Committee.find(contents['project'])
pmc_email = "#{pmc.mail_list}@apache.org"
subject = '[RESULT]' + contents['subject']
last_votes = Hash.new
votes = contents['votes']
votes.each { |v|
last_votes[v['member']] = [v['vote'], v['timestamp']]
}
counts=Hash.new(0)
last_votes.each { |k,v|
counts[v[0]] += 1
}
started = Time.parse votes[0]['timestamp']
elapsed=(Time.now - started) / (60*60)
body_text = <<-EOD.gsub(/^ /,'') # Eclipse plugin does not like heredoc squiggle
Here is the tally of the latest votes from each voter:
#{last_votes.map{ |k,v| "Member: #{k.ljust(20)} Vote: #{v[0]} Date: #{v[1]}"}.join("\n")}
Vote Count
#{counts.sort_by{|k,v| VALID_VOTES.index(k) || 99}.map{|k,v| " #{k} #{v.to_s.rjust(5)}"}.join("\n")}
Vote started: #{started.to_s[0..9]} Hours elapsed: #{elapsed.to_i}
EOD
_body_text body_text # debug
# create the email to the p(pmc)
mail = Mail.new do
to pmc_email
from user_email
cc user_email
subject subject
text_part do
body body_text
end
end
mail.deliver
end
def update()
# setup and validation
raise ArgumentError.new('token must not be nil') unless @token
raise ArgumentError.new("Invalid action: '#{@action}'") unless VALID_ACTIONS.include? @action
raise ArgumentError.new('expectedPhase must not be nil') unless @expectedPhase
if @newPhase and not VALID_PHASES.include? @newPhase
raise ArgumentError.new("Invalid @newPhase: '#{@newPhase}'")
end
timestamp = Time.now.utc.to_s
addComment = nil
voteinfo = nil
if @action == 'submitVote'
raise ArgumentError.new("Invalid vote: '#{@vote}'") unless VALID_VOTES.include? @vote
raise ArgumentError.new('member must not be nil') unless @member
if @vote == '-1'
raise ArgumentError.new('-1 vote must have comment') unless @comment
end
if @comment # allow comment for other votes
voteinfo = {
'vote' => @vote,
'comment' => @comment,
'member' => @member,
'timestamp' => timestamp,
}
else
voteinfo = {
'vote' => @vote,
'member' => @member,
'timestamp' => timestamp,
}
end
elsif HAS_COMMENT.include? @action
if @comment
addComment =
{
'comment' => @comment,
'member' => @member,
'timestamp' => timestamp,
}
else
raise ArgumentError.new("comment must not be nil for '#{@action}'")
end
end
file = "/srv/icla/#{@token}.json"
# now read/update the file if necessary
contents = {} # define the var outside the block
rewrite = false # should the file be updated?
phases = *@expectedPhase # convert string to array
LockFile.lockfile(file, 'r+', File::LOCK_EX) do |f|
contents = JSON::parse(f.read)
phase = contents['phase']
raise ArgumentError.new("Phase '#{phase}': expected '#{@expectedPhase}'") unless @expectedPhase == '*' or phases.include? phase
if @newPhase && @newPhase != phase
contents['phase'] = @newPhase
rewrite = true
end
if @action == 'startVoting' # need to add a vote to start this off
comment0 = contents['comments'][0]['comment'] # initial comment
voteinfo = {
'vote' => '+1',
'comment' => "#{comment0}\nHere is my +1", # append to original comment
'member' => @member,
'timestamp' => timestamp,
}
addComment['comment'] += "\n**Starting the vote.**"
end
if voteinfo
contents['votes'] << voteinfo
rewrite = true
end
if addComment
contents['comments'] << addComment
rewrite = true
end
if rewrite
f.rewind # back to start
f.truncate(0) # need to empty the file otherwise can result in leftover data
f.write(JSON.pretty_generate(contents))
end
end
if @action == 'tallyVote'
sendTally(contents)
end
# return the data
_rewrite rewrite # debug
contents
end
# error handler
def process()
contents = {}
begin
contents = update
rescue => e
_error e
_backtrace e.backtrace[0] # can be rather large
end
_contents contents
end
if __FILE__ == $0 # Allow independent testing
require 'whimsy/asf'
require 'mail'
ret = {}
# method_missing caused some errors to be overlooked
%w{backtrace body_text error contents rewrite}.each do |n|
define_method("_#{n}") do |a|
ret[n] = a
end
end
params = Hash[*ARGV] # cannot combine this with next line as hash doesn't yet exist
params.each{|k,v| params[k] = v.split(',') if v =~ /,/} # fix up lists
params.each{|k,v| instance_variable_set("@#{k}", v)}
puts params.inspect
if @action == 'sendTally' # special for testing stand-alone
contents = JSON.parse(File.read("/srv/icla/#{@token}.json"))
begin
sendTally(contents)
rescue => err
puts err
end
puts ret['body_text']
else
process
puts JSON.pretty_generate(ret) # output the return data
end
else
process
end