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
+