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