This is an automated email from the ASF dual-hosted git repository.

curcuru pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/whimsy.git


The following commit(s) were added to refs/heads/master by this push:
     new 03db14f  Restructure and update proxy form
03db14f is described below

commit 03db14f61f7fa80b7856003d73adfb7300c09fed
Author: Shane Curcuru <[email protected]>
AuthorDate: Sun Jan 19 18:47:31 2020 -0500

    Restructure and update proxy form
    
    Detects when next meeting not available; moves common functions to new 
file; improve output display
---
 www/members/meeting-util.rb |  61 +++++++
 www/members/proxy.cgi       | 401 +++++++++++++++++++++-----------------------
 2 files changed, 255 insertions(+), 207 deletions(-)

diff --git a/www/members/meeting-util.rb b/www/members/meeting-util.rb
new file mode 100644
index 0000000..6170f57
--- /dev/null
+++ b/www/members/meeting-util.rb
@@ -0,0 +1,61 @@
+# Utility methods and structs related to Member's Meetings
+# NOTE: Assumes 21st century
+
+class MeetingUtil
+
+  # Calculate how many members required to attend first half for quorum
+  def self.calculate_quorum(mtg_dir)
+    begin
+      num_members = File.read(File.join(mtg_dir, 'record')).each_line.count
+      quorum_need = num_members / 3
+      num_proxies = Dir[File.join(mtg_dir, 'proxies-received', '*')].count
+      attend_irc = quorum_need - num_proxies
+    rescue StandardError => e
+      # Ensure we can't break rest of script
+      puts "ERROR: #{e}"
+      return 0, 0, 0, 0
+    end
+    return num_members, quorum_need, num_proxies, attend_irc
+  end
+  
+  # Get info about current users's proxying
+  # @return "help text", ["id | name (proxy)", ...] if they are a proxy
+  # @return "You have already submitted a proxy form"
+  # @return nil otherwise
+  def self.is_user_proxied(mtg_dir, id)
+    user = ASF::Person.find(id)
+    lines = IO.read(File.join(mtg_dir, 'proxies'))
+    proxylist = lines.scan(/\s\s(.{25})(.*?)\((.*?)\)/) # [["Shane Curcuru    
", "David Fisher ", "wave"], ...]
+    help = nil
+    copypasta = [] # theiravailid | Their Name in Rolls (proxy)
+    begin
+      proxylist.each do |arr|
+### ### ###       if user.cn == arr[0].strip
+        if 'curcuru' == arr[0].strip
+          copypasta << "#{arr[2].ljust(12)} | #{arr[1].strip} (proxy)"
+        elsif user.id == arr[2]
+          help = "NOTE: You have already submitted a proxy form for 
#{arr[0].strip} to mark your attendance (be sure they know to mark you at Roll 
Call)! "
+        end
+      end
+    rescue StandardError => e
+      (help ||= "") << "ERROR, could not read LDAP, proxy data may not be 
correct: #{e.message}"
+    end
+    if copypasta.empty?
+      return help
+    else
+      (help ||= "") << "Since you are a proxy for others, then AFTER the 2. 
Roll Call is called, you may copy/paste the below lines to mark your and your 
proxies attendance."
+      copypasta.unshift("#{user.id.ljust(12)} | #{user.cn}")
+      return help, copypasta
+    end
+  end
+  
+  # Read mapping of labels to fields
+  def self.get_latest(mtg_root)
+    return Dir[File.join(mtg_root, '2*')].sort.last
+  end
+  # Read mapping of labels to fields
+  def self.get_previous(mtg_root)
+    return Dir[File.join(mtg_root, '2*')].sort[-2]
+  end
+
+end
\ No newline at end of file
diff --git a/www/members/proxy.cgi b/www/members/proxy.cgi
index f2ae90c..551aa17 100755
--- a/www/members/proxy.cgi
+++ b/www/members/proxy.cgi
@@ -1,160 +1,107 @@
 #!/usr/bin/env ruby
+PAGETITLE = "Member Meeting Proxy Selection Form" # Wvisible:meeting
 $LOAD_PATH.unshift '/srv/whimsy/lib'
 
-require 'wunderbar'
 require 'whimsy/asf'
+require 'wunderbar'
+require 'wunderbar/bootstrap'
+require 'wunderbar/jquery'
 require 'date'
 require 'tmpdir'
+require_relative 'meeting-util'
 
-MEETINGS = ASF::SVN['Meetings']
-meeting = File.basename(Dir[File.join(MEETINGS, '2*')].sort.last).untaint
+# TODO: Read in proxies between Volunteers: and Assignments: lines
 volunteers = [
-  "Phil Steitz (psteitz)",
   "Shane Curcuru (curcuru)",
-  "Michael Brohl (mbrohl)",
-  "Jim Jagielski (jim)",
-  "Daniel Ruggeri (druggeri)",
-  "Greg Stein (gstein)",
-  "Craig L Russell (clr)",
-  "Bertrand Delacretaz (bdelacretaz)"
+  "Craig L Russell (clr)"
 ]
 
-# Calculate how many members required to attend first half for quorum
-def calculate_quorum(meeting)
-  begin
-    num_members = File.read(File.join(MEETINGS, meeting, 
'record')).each_line.count
-    quorum_need = num_members / 3
-    num_proxies = Dir[File.join(MEETINGS, meeting, 'proxies-received', 
'*')].count
-    attend_irc = quorum_need - num_proxies
-  rescue StandardError => e
-    # Ensure we can't break rest of script
-    puts "ERROR: #{e}"
-    return 0, 0, 0, 0
+# Emit basic instructions and details on quorum
+def emit_instructions(today, cur_mtg_dir, meeting)
+  if today > meeting
+    _p.text_warning %{
+        WARNING: Data for the next Member's Meeting is not yet available, 
+        so this form will not work yet.  Please wait until the Chairman 
+        announces the opening of nominations for the board and new members,
+        and then check back to assign a new proxy for the meeting.
+        Data from the previous meeting on #{meeting} is shown below for 
debugging only.
+      }
   end
-  return num_members, quorum_need, num_proxies, attend_irc
-end
-
-# Get info about current users's proxying
-# @return "help text", ["id | name (proxy)", ...] if they are a proxy
-# @return "You have already submitted a proxy form"
-# @return nil otherwise
-def is_user_proxied(meeting, id)
-  user = ASF::Person.find(id)
-  lines = IO.read(File.join(MEETINGS, meeting, 'proxies'))
-  proxylist = lines.scan(/\s\s(.{25})(.*?)\((.*?)\)/) # [["Shane Curcuru    ", 
"David Fisher ", "wave"], ...]
-  help = nil
-  copypasta = [] # theiravailid | Their Name in Rolls (proxy)
-  proxylist.each do |arr|
-    if user.cn == arr[0].strip
-      copypasta << "#{arr[2].ljust(12)} | #{arr[1].strip} (proxy)"
-    elsif user.id == arr[2]
-      help = "NOTE: You have already submitted a proxy form for 
#{arr[0].strip} to mark your attendance (be sure they know to mark you at Roll 
Call)! "
+  _p %{
+    The bottom of this form allows you to assign an attendance proxy for the 
upcoming 
+    Member's Meeting on #{meeting}. If there is any chance you might not be 
able 
+    to attend the first part of the Member's Meeting on Tuesday, then 
+    please assign a proxy, because that helps the meeting reach 
+    quorum more quickly. 
+    You can still attend the meeting if you want, and can revoke a 
+    proxy at any time.
+  }
+  _p %{
+    If you submit a proxy, you will still be sent board and new member ballots 
by email 
+    during the meeting's 46 hour recess (between Tuesday and Thursday, 
+    with two hours for vote counting), so you will still need to 
+    cast your votes by checking your mail during the recess. If 
+    you won't have internet access the week of the meeting, ask 
+    for how to assign a proxy for your vote ballots as well.
+  }
+  num_members, quorum_need, num_proxies, attend_irc = 
MeetingUtil.calculate_quorum(cur_mtg_dir)
+  if num_members
+    _p do
+      _ 'Currently, we must have '
+      _span.text_primary "#{attend_irc}" 
+      _ " Members attend the first half of the #{meeting} meeting and respond 
to Roll Call to reach quorum and continue the meeting."
+      _ " Calculation: Total voting members: #{num_members}, with one third 
for quorum: #{quorum_need}, minus previously submitted proxies: #{num_proxies}"
     end
   end
-  if copypasta.empty?
-    return help
-  else
-    (help ||= "") << "Since you are a proxy for others, then AFTER the 2. Roll 
Call is called, you may copy/paste the below lines to mark your and your 
proxies attendance."
-    copypasta.unshift("#{user.id.ljust(12)} | #{user.cn}")
-    return help, copypasta
-  end
 end
 
-_html do
-  _link href: "css/bootstrap.min.css", rel: 'stylesheet'
-  _link href: "css/bootstrap-combobox.css", rel: 'stylesheet'
-  _style :system
-  _style %{
-    .transcript {margin: 0 16px}
-    .transcript pre {border: none; line-height: 0}
-  }
-
-
-  # get a list of members who have submitted proxies
-  exclude = Dir[File.join(MEETINGS, meeting,'proxies-received', '*')].
-    map {|name| name[/(\w+)\.\w+$/, 1]}
-
-  if _.get?
-
-    _div_.container do
-      _div.row do
-        _div.well.text_center do
-          _h1 'Member Meeting Proxy Selection Form'
-          _h3 Date.parse(meeting).strftime("%B %-d, %Y")
-        end
-      end
-
-      _div.row do
-        _div do
-          _p %{
-            This form allows you to assign an attendance proxy for the 
upcoming 
-            Member's Meeting. If there is any chance you might not be able 
-            to attend the first part of the Member's Meeting on Tuesday, then 
-            please assign a proxy, because that helps the meeting reach 
-            quorum more quickly. 
-            You can still attend the meeting if you want, and can revoke a 
-            proxy at any time.
-          }
-          _p %{
-            You will still be sent board and new member ballots by email 
-            during the meeting's 46 hour recess (between Tuesday and Thursday, 
-            with two hours for vote counting), so you will still need to 
-            cast your votes by checking your mail during the recess. If 
-            you won't have internet access the week of the meeting, ask 
-            for how to assign a proxy for your vote ballots as well.
-          }
-          num_members, quorum_need, num_proxies, attend_irc = 
calculate_quorum(meeting)
-          if num_members
-            _p do
-              _ 'Currently, we must have '
-              _span.text_primary "#{attend_irc}" 
-              _ ' Members attend the first half of the meeting and respond to 
Roll Call to reach quorum and continue the meeting.'
-              _ " Calculation: Total voting members: #{num_members}, with one 
third for quorum: #{quorum_need}, minus previously submitted proxies: 
#{num_proxies}"
-            end
-          end
-          help, copypasta = is_user_proxied(meeting, $USER)
-          if help
-            _p help
-            if copypasta
-              _ul.bg_success do
-                copypasta.each do |copyline|
-                  _pre copyline
-                end
-              end
-            end
-          else
-            _p 'The following members have volunteered to serve as proxies; 
you can freely select any one of them below:'
-            _ul do
-              volunteers.each do |vol|
-                _pre vol
+# Emit meeting data and form for user to select a proxy - GET
+def emit_form(cur_mtg_dir, meeting, volunteers)
+  help, copypasta = MeetingUtil.is_user_proxied(cur_mtg_dir, $USER)
+  _whimsy_panel("Select A Proxy For Upcoming Meeting", style: 'panel-success') 
do
+    _div.row do
+      _div do
+        if help
+          _p help
+          if copypasta
+            _ul.bg_success do
+              copypasta.each do |copyline|
+                _pre copyline
               end
             end
           end
-          _p do
-            _ "IMPORTANT! Be sure to tell the person that you select as proxy 
that you've assigned them to mark your attendance! They simply need to mark 
your proxy attendance when the meeting starts."
-            _a 'Read full procedures for Member Meeting', href: 
'https://www.apache.org/foundation/governance/members.html#meetings'
+        else
+          _p 'The following members have volunteered to serve as proxies; you 
can freely select any one of them below:'
+          _ul do
+            volunteers.each do |vol|
+              _pre vol
+            end
           end
         end
+        _p do
+          _ "IMPORTANT! Be sure to tell the person that you select as proxy 
that you've assigned them to mark your attendance! They simply need to mark 
your proxy attendance when the meeting starts."
+          _a 'Read full procedures for Member Meeting', href: 
'https://www.apache.org/foundation/governance/members.html#meetings'
+        end
       end
-
+      
       _div.row do
         _div do
-          _pre IO.read(File.join(MEETINGS, meeting, 'member_proxy.txt'))
+          _pre IO.read(File.join(cur_mtg_dir, 'member_proxy.txt').untaint)
         end
       end
-
+      
       _form method: 'POST' do
         _div_.row do
           _div.form_group do
             _label 'Select proxy'
-
+            
             # Fetch LDAP
             ldap_members = ASF.members
             ASF::Person.preload('cn', ldap_members)
-
+            
             # Fetch members.txt
             members_txt = ASF::Member.list
-
+            
             _select.combobox.input_large.form_control name: 'proxy' do
               _option 'Select an ASF Member', :selected, value: ''
               ldap_members.sort_by(&:public_name).each do |member|
@@ -168,7 +115,7 @@ _html do
             end
           end
         end
-
+        
         _div_.row do
           _div.button_group.text_center do
             _button.btn.btn_primary 'Submit'
@@ -176,117 +123,157 @@ _html do
         end
       end
     end
-
-    _script src: "js/jquery-1.11.1.min.js"
-    _script src: "js/bootstrap.min.js"
-    _script src: "js/bootstrap-combobox.js"
-
+    
+##    _script src: "js/jquery-1.11.1.min.js"
+##    _script src: "js/bootstrap.min.js"
+    _script src: "js/bootstrap-combobox.js" # TODO do we need this still?
+    
     _script_ %{
       // convert select into combobox
       $('.combobox').combobox();
-
+      
       // initially disable submit
       $('.btn').prop('disabled', true);
-
+      
       // enable submit when proxy is chosen
       $('*[name="proxy"]').change(function() {
         $('.btn').prop('disabled', false);
-      });
+        });
     }
+  end
+end
 
-  else
-    _body? do
-      _h3_ 'Proxy Assignment - Session Transcript'
-
-      # collect data
-      proxy = File.read(File.join(MEETINGS, meeting, 'member_proxy.txt'))
-      user = ASF::Person.find($USER)
-      date = Date.today.strftime("%B %-d, %Y")
+# Emit a record of a user's submission - POST
+def emit_post(cur_mtg_dir, meeting)
+  _h3_ 'Proxy Assignment - Session Transcript'
 
-      # update proxy form (match as many _ as possible up to the name length)
-      proxy[/authorize _(_{,#{@proxy.length}})/, 1] = @proxy.gsub(' ', '_')
+  # collect data
+  proxy = File.read(File.join(cur_mtg_dir, 'member_proxy.txt'))
+  user = ASF::Person.find($USER)
+  date = Date.today.strftime("%B %-d, %Y")
 
-      proxy[/signature: _(_#{'_' *user.public_name.length}_)/, 1] = 
-        "/#{user.public_name.gsub(' ', '_')}/"
+  # update proxy form (match as many _ as possible up to the name length)
+  proxy[/authorize _(_{,#{@proxy.length}})/, 1] = @proxy.gsub(' ', '_')
 
-      proxy[/name: _(#{'_' *user.public_name.length})/, 1] = 
-        user.public_name.gsub(' ', '_')
+  proxy[/signature: _(_#{'_' *user.public_name.length}_)/, 1] = 
+    "/#{user.public_name.gsub(' ', '_')}/"
 
-      proxy[/availid: _(#{'_' *user.id.length})/, 1] = 
-        user.id.gsub(' ', '_')
+  proxy[/name: _(#{'_' *user.public_name.length})/, 1] = 
+    user.public_name.gsub(' ', '_')
 
-      proxy[/Date: _(#{'_' *date.length})/, 1] = date.gsub(' ', '_')
+  proxy[/availid: _(#{'_' *user.id.length})/, 1] = 
+    user.id.gsub(' ', '_')
 
-      proxyform = proxy.untaint
+  proxy[/Date: _(#{'_' *date.length})/, 1] = date.gsub(' ', '_')
 
-      # report on commit
-      _div.transcript do
-        Dir.mktmpdir do |tmpdir|
-          svn = `svn info #{MEETINGS}/#{meeting}`[/URL: (.*)/, 1]
+  proxyform = proxy.untaint
 
-          _.system [
-            'svn', 'checkout', '--quiet', svn.untaint, tmpdir.untaint,
-            ['--no-auth-cache', '--non-interactive'],
-            (['--username', $USER, '--password', $PASSWORD] if $PASSWORD)
-          ]
+  # report on commit
+  _div.transcript do
+    Dir.mktmpdir do |tmpdir|
+      svn = `svn info #{MEETINGS}/#{meeting}`[/URL: (.*)/, 1]
 
-          Dir.chdir(tmpdir) do
-            # write proxy form
-            filename = "proxies-received/#$USER.txt".untaint
-            File.write(filename, proxyform)
-            _.system ['svn', 'add', filename]
-            _.system ['svn', 'propset', 'svn:mime-type',
-              'text/plain; charset=utf-8', filename]
+      _.system [
+        'svn', 'checkout', '--quiet', svn.untaint, tmpdir.untaint,
+        ['--no-auth-cache', '--non-interactive'],
+        (['--username', $USER, '--password', $PASSWORD] if $PASSWORD)
+      ]
 
-            # get a list of proxies
-            list = Dir['proxies-received/*.txt'].map do |file|
-              form = File.read(file.untaint)
-        
-              id = file[/([-A-Za-z0-9]+)\.\w+$/, 1]
-              proxy = form[/hereby authorize ([\S].*) to act/, 1].
-                gsub('_', ' ').strip
-              # Ensure availid is not included in proxy name here
-              proxy = proxy[/([^(]+)/, 1].strip
-              name = form[/signature: ([\S].*)/, 1].gsub(/[\/_]/, ' ').strip
+      Dir.chdir(tmpdir) do
+        # write proxy form
+        filename = "proxies-received/#$USER.txt".untaint
+        File.write(filename, proxyform)
+        _.system ['svn', 'add', filename]
+        _.system ['svn', 'propset', 'svn:mime-type',
+          'text/plain; charset=utf-8', filename]
 
-              "   #{proxy.ljust(24)} #{name} (#{id})"
-            end
+        # get a list of proxies
+        list = Dir['proxies-received/*.txt'].map do |file|
+          form = File.read(file.untaint)
     
-            # gather a list of all non-text proxies (TODO unused)
-            nontext = Dir['proxies-received/*'].
-              reject {|file| file.end_with? '.txt'}.
-              map {|file| file[/([-A-Za-z0-9]+)\.\w+$/, 1]}
+          id = file[/([-A-Za-z0-9]+)\.\w+$/, 1]
+          proxy = form[/hereby authorize ([\S].*) to act/, 1].
+            gsub('_', ' ').strip
+          # Ensure availid is not included in proxy name here
+          proxy = proxy[/([^(]+)/, 1].strip
+          name = form[/signature: ([\S].*)/, 1].gsub(/[\/_]/, ' ').strip
+
+          "   #{proxy.ljust(24)} #{name} (#{id})"
+        end
 
-            # update proxies file
-            proxies = IO.read('proxies')
-            existing = proxies.scan(/   \S.*\(\S+\).*$/)
-            existing_ids = existing.map {|line| line[/\((\S+)\)/, 1] }
-            added = list.
-              reject {|line| existing_ids.include? line[/\((\S+)\)$/, 1]}
-            list = added + existing
-            proxies[/.*-\n(.*)/m, 1] = list.flatten.sort.join("\n") + "\n"
+        # gather a list of all non-text proxies (TODO unused)
+        nontext = Dir['proxies-received/*'].
+          reject {|file| file.end_with? '.txt'}.
+          map {|file| file[/([-A-Za-z0-9]+)\.\w+$/, 1]}
+
+        # update proxies file
+        proxies = IO.read('proxies')
+        existing = proxies.scan(/   \S.*\(\S+\).*$/)
+        existing_ids = existing.map {|line| line[/\((\S+)\)/, 1] }
+        added = list.
+          reject {|line| existing_ids.include? line[/\((\S+)\)$/, 1]}
+        list = added + existing
+        proxies[/.*-\n(.*)/m, 1] = list.flatten.sort.join("\n") + "\n"
+
+        IO.write('proxies', proxies)
+
+        # commit
+        _.system [
+          'svn', 'commit', filename, 'proxies',
+          '-m', "assign #{@proxy} as my proxy",
+          ['--no-auth-cache', '--non-interactive'],
+          (['--username', $USER, '--password', $PASSWORD] if $PASSWORD)
+        ]
+        # TODO: send email to @proxy per WHIMSY-78
+      end
+    end
 
-            IO.write('proxies', proxies)
+    # report on contents
+    _h3! do
+      _span "Contents of "
+      _code "foundation/meetings/#{meeting}/#{$USER}.txt"
+      _span ":"
+    end
 
-            # commit
-            _.system [
-              'svn', 'commit', filename, 'proxies',
-              '-m', "assign #{@proxy} as my proxy",
-              ['--no-auth-cache', '--non-interactive'],
-              (['--username', $USER, '--password', $PASSWORD] if $PASSWORD)
-            ]
-          end
-        end
-      end
+    _pre proxyform
+  end
+end
 
-      # report on contents
-      _h3! do
-        _span "Contents of "
-        _code "foundation/meetings/#{meeting}/#{$USER}.txt"
-        _span ":"
+# produce HTML
+_html do
+  _style :system
+  _style %{
+    .transcript {margin: 0 16px}
+    .transcript pre {border: none; line-height: 0}
+  }
+  _body? do
+    # Find latest meeting and check if it's in the future yet
+    MEETINGS = ASF::SVN['Meetings']
+    cur_mtg_dir = MeetingUtil.get_latest(MEETINGS).untaint
+    meeting = File.basename(cur_mtg_dir)
+    today = Date.today.strftime('%Y%m%d')
+    # get a list of members who have submitted proxies
+    exclude = Dir[File.join(cur_mtg_dir,'proxies-received', '*')].
+      map {|name| name[/(\w+)\.\w+$/, 1]}
+    _whimsy_body(
+      title: PAGETITLE,
+      subtitle: today > meeting ? "ERROR: Next Meeting Data Not Available" : 
"How To Assign A Proxy For Upcoming Meeting",
+      related: {
+        '/members/meeting' => 'How-To / FAQ for Member Meetings',
+        '/members/attendance-xcheck' => 'Members Meeting Attendance 
Crosscheck',
+        '/members/inactive' => 'Inactive Member Feedback Form',
+        '/members/subscriptions' => 'Members@ Mailing List Crosscheck'
+      },
+      helpblock: -> {
+        emit_instructions(today, cur_mtg_dir, meeting)
+      }
+    ) do
+      if _.get?
+        emit_form(cur_mtg_dir, meeting, volunteers)
+      else # POST
+        emit_post(cur_mtg_dir, meeting)
       end
-
-      _pre proxyform
     end
   end
 end
+

Reply via email to