www/members/meeting.cgi (336 lines of code) (raw):

#!/usr/bin/env ruby PAGETITLE = "Member's Meeting Information" # Wvisible:meeting $LOAD_PATH.unshift '/srv/whimsy/lib' require 'whimsy/asf' require 'wunderbar/bootstrap' require 'time' require 'json' require 'wunderbar/jquery/stupidtable' require 'whimsy/asf/meeting-util' DTFORMAT = '%A, %d %B %Y at %H:%M %z' TADFORMAT = '%Y%m%dT%H%M%S' WDAYFORMAT = '%A' # Utility function for links, Note: cheezy path detection within MEETING_FILES def emit_link(cur_mtg_dir, f, desc) _a desc, href: f.include?('/') && !f.start_with?('runbook/') ? f : File.join(cur_mtg_dir, f) end # Output action links for meeting records, depending on if current or past # @param dt Time meeting starts def emit_meeting(cur_mtg_dir, svn_mtg_dir, dt, num_members, quorum_need, num_proxies, attend_irc) _div id: "meeting-#{dt.year}" _whimsy_panel("All Meeting Details for #{dt.strftime(DTFORMAT)}", style: 'panel-info') do if Time.now > dt _p do _ 'At the time of this past meeting, we had:' _ul do _li "#{num_members} eligible voting Members," _li "#{quorum_need} needed for quorum (one third)," _li "#{num_proxies} proxy assignments available for the meeting," _li "And hoped that at least #{attend_irc} would attend the start of meeting." end attendees_file = File.join(cur_mtg_dir, 'attend') if File.exist?(attendees_file) attendees = File.readlines(attendees_file) _ "By the end of the meeting, we had a total of #{attendees.count} Members participating (either via attending IRC, sending a proxy, or voting via email)" else _p.alert.alert_danger do _span "Unable to calculate participating members (" _code "attend" _span "file does not yet exist for meeting)" end end end _p "These are historical links to the past meeting's record." else _p "Live links to the upcoming meeting records/ballots/how-tos are below." end _ul do ASF::MeetingUtil::MEETING_FILES.each do |f, desc| _li do emit_link(svn_mtg_dir, f, desc) end end end end end MEETINGS = ASF::SVN['Meetings'] RECORDS_DIR = ASF::MeetingUtil::RECORDS # produce HTML _html do _body? do last_mtg_dir = ASF::MeetingUtil.get_latest_completed(MEETINGS) last_mtg_date = Time.parse(File.basename(last_mtg_dir)) cur_mtg_dir = ASF::MeetingUtil.get_latest(MEETINGS) meeting = File.basename(cur_mtg_dir) svn_mtg_dir = File.join(RECORDS_DIR, meeting) # Calculate quorum num_members, quorum_need, num_proxies, attend_irc = ASF::MeetingUtil.calculate_quorum(cur_mtg_dir) proxy_nominees = ASF::MeetingUtil.getProxyNominees.count mtg_timeline = ASF::MeetingUtil.get_timeline(cur_mtg_dir) meeting_start_time = Time.parse(mtg_timeline['meeting_start_iso']) meeting_type = mtg_timeline.fetch('meeting_type', 'Annual') _whimsy_body( title: PAGETITLE, subtitle: 'Member Meeting Overview', relatedtitle: 'Meeting Explainers', related: { '/members/proxy' => 'PLEASE Assign A Proxy For The Meeting', "#{svn_mtg_dir}/README.txt" => "Detailed README for this meeting", "#{svn_mtg_dir}/agenda.txt" => "Actual Agenda for this meeting", 'https://www.apache.org/foundation/governance/meetings' => 'Descriptions: how voting works:', 'https://www.apache.org/foundation/governance/meetings#how-member-votes-are-tallied' => 'New Members Elected By Majority', 'https://www.apache.org/foundation/governance/meetings#how-votes-for-the-board-are-tallied' => 'Board Seats Are Elected By STV', '/members/whatif' => 'Explore Past Board STV Results', '/members/non-participants' => 'Members Not Participating Recently', '/members/inactive' => 'Inactive Member Feedback Form', RECORDS_DIR => 'Official Past Meeting Records', 'https://lists.apache.org/list.html?members@apache.org' => 'Read the members@ List Archives' }, helpblock: -> { if Time.now > meeting_start_time _p do _ %{ The last Member's Meeting was held #{last_mtg_date.strftime('%A, %d %B %Y')}. Expect the next Annual Member's meeting to be scheduled between 12 - 13 months after the previous Annual meeting, as per } _a 'the bylaws 3.2.', href: 'https://www.apache.org/foundation/bylaws.html#3.2' _ 'Stay tuned for a [NOTICE] ' _a 'email on members-notify@', href: 'https://lists.apache.org/list?members-notify@apache.org' _ ' announcing the next meeting. The below information is about the ' _strong 'LAST' _ " Member's meeting." end else _p do if /test/i =~ meeting_type _strong "NOTICE NOTICE NOTICE: This is TEST MEETING DATA ONLY - NOT AN ACTUAL MEETING :NOTICE NOTICE NOTICE" _br end _ "The next #{meeting_type} Member's Meeting starts at " _a href: "http://www.timeanddate.com/worldclock/fixedtime.html?iso=#{meeting_start_time.strftime(TADFORMAT)}" do _span.glyphicon.glyphicon_time '' _ " #{meeting_start_time.strftime(DTFORMAT)} " end _ 'as an online meeting in the' _strong ' NEW ' _a 'ASFMM.apache.org chat tool', href: 'https://asfmm.apache.org' _ ' for less than an hour. Most activity happens in the weeks before the meeting. Please review the meeting timeline, and some NEW PROCESS steps, or ' _a 'see detailed FAQ about meeting files.', href: "#{svn_mtg_dir}/README.txt" if /test/i =~ meeting_type _br _strong "NOTICE NOTICE NOTICE: This is TEST MEETING DATA ONLY - NOT AN ACTUAL MEETING :NOTICE NOTICE NOTICE" end end _ul do _li do _a 'Nominations Open', href: '#nominations' _ ": #{mtg_timeline['nominations_open_date']} - Seconds and Statements may be added" end _li "Nominations Close: #{mtg_timeline['nominations_close_date']} - no further nominations allowed" _li do _a 'Record Date set', href: '#seconds' _ ": #{mtg_timeline['nominations_notice_date']} - New Member candidates list sent to members-notify@" end _li "Election Locked: #{mtg_timeline['vote_create_date']} - no further changes to Seconds, Statements" _li do _a 'Polls Open', href: '#recess' _ ": #{mtg_timeline['vote_open_date']} - vote online at vote.apache.org" end _li "Polls Close: #{mtg_timeline['polls_close_date']} - vote early!" _li do _a 'Online Meeting', href: '#secondhalf' _ ": #{mtg_timeline['meeting_iso']} - quorum required here; expect a short meeting" end _li do _a 'New Member Applications', href: '#after' _ " due before: #{mtg_timeline['member_form_date']}" end end _p do _ 'Currently, we will need ' _span.text_primary attend_irc _ " Members who have NOT submitted a proxy to login to the online meeting #{meeting_start_time.strftime(WDAYFORMAT)} to reach quorum and continue the meeting." _br _ "Calculation: Total voting members: #{num_members}; one third for quorum: #{quorum_need}; minus previously submitted proxies: #{num_proxies}" end end } ) do help, copypasta = ASF::MeetingUtil.is_user_proxied(cur_mtg_dir, $USER) user = ASF::Person.find($USER) _div id: 'personal' _whimsy_panel("#{user.public_name} - Personal Details For Meeting #{meeting_start_time.strftime(DTFORMAT)}", style: 'panel-primary') do _p do if help _p help if copypasta _ul.bg_success do copypasta.each do |copyline| _pre copyline end end end else _ 'You have not submitted a proxy - even if you can attend the the meeting, ' _strong 'please' _a " assign a proxy - it's easy!", href: '/members/proxy' end end end _div id: 'nominations' _whimsy_panel("Timeline: Nominations and Seconds Period", style: 'panel-default') do _p do _ 'Before an Annual meeting, Members may nominate candidates for the Board election, or as New Member Candidates. Nominations are only official if placed in the correct files; although much discussion also happens on members@.' _ul do ['/members/nominate_board.cgi', '/members/nominate_member.cgi', '/members/proxy.cgi', '/members/check_boardnoms.cgi', '/members/check_membernoms.cgi'].each do |f| _li do emit_link(svn_mtg_dir, f, ASF::MeetingUtil::MEETING_FILES[f]) end end end _ 'During this period, Members may add Seconds Statements of their own personal recommendations to various nominees.' _ 'Director Nominees should decide to accept the nomination, and then ' _a 'add a Candidate Statement to their ballot', href: "#{svn_mtg_dir}/runbook/director_ballot_email.txt" _br _strong "Nominations close at: #{mtg_timeline['nominations_close_date']}!" end end _div id: 'seconds' _whimsy_panel("Timeline: Record Date and Nominees NOTICE email (10 days before meeting)", style: 'panel-default') do _p do _ "Record date: #{mtg_timeline['nominations_notice_date']}" end _p do _ '10 days before the meeting, per bylaws 3.7 the official list of eligible Members is fixed, and the NOTICE of any candidates nominated for Membership will be sent out to members-notify@.' _ 'No further names may be added to nominations, and director candidates should have ' _a 'added their Candidate Statement to their ballot', href: "#{svn_mtg_dir}/runbook/director_ballot_email.txt" _ ' before the official ballots are frozen.' _ul do ['agenda.txt', '/members/check_boardnoms.cgi', '/members/check_membernoms.cgi'].each do |f| _li do emit_link(svn_mtg_dir, f, ASF::MeetingUtil::MEETING_FILES[f]) end end end _strong "Ballot files are frozen for vote creation at: #{mtg_timeline['vote_create_date']}!" end end _div id: 'firsthalf' # Pre-2022 meeting anchor _div id: 'recess' _whimsy_panel("Timeline: Voting By Email (until 24 hours before meeting)", style: 'panel-info') do _p do _strong 'NEW PROCESS simplified vote.apache.org process!' _ "Polls will open #{mtg_timeline['vote_open_date']}, and the vote monitors will send eligible voters an email " _code 'From: voter@apache.org' _ ' with updated instructions on voting.' _ 'Voting is now done by simply logging into vote.apache.org with your Apache ID - no more need for a voting key in email!' end _p do _strong 'REMEMBER:' _ "Ballots close at #{mtg_timeline['polls_close_date']} - ONE FULL DAY (24 hours) BEFORE the meeting starts - don't wait to vote!" end _p do _ul do _li do _a 'New Members are Elected By Majority Yes/No/Abstain vote, when elections held', href: 'https://www.apache.org/foundation/governance/meetings#membervoting' end _li do _a 'Board Seats Are Elected By STV at Annual Meetings - ORDER OF YOUR VOTE MATTERS!', href: 'https://www.apache.org/foundation/governance/meetings#boardvoting' end _li do _a 'Cast Your Votes (requires ASF Member Login)', href: "#{mtg_timeline['meeting_vote_link']}" end end _strong "Polls close: #{mtg_timeline['polls_close_date']}!" end end _div id: 'secondhalf' _whimsy_panel("Timeline: Online Meeting (at #{meeting_start_time.strftime(DTFORMAT)})", style: 'panel-primary') do _p do _a href: "http://www.timeanddate.com/worldclock/fixedtime.html?iso=#{meeting_start_time.strftime(TADFORMAT)}" do _span.glyphicon.glyphicon_time '' _em '(meeting time in various zones)' end _ 'The single online meeting on asfmm.apache.org is typically short - it\'s primarily briefly reporting from officers, announcing vote results and any last-minute announcements. Members do not need to attend the meeting if you proxied or voted; all results will be emailed or checked into SVN.' _ul do _li do _a 'ASFMM meeting tool (requires ASF Member Login)', href: 'https://asfmm.apache.org/' end ['record', 'attend', 'voter-tally', 'raw_board_votes.txt', 'raw-irc-log'].each do |f| _li do emit_link(svn_mtg_dir, f, ASF::MeetingUtil::MEETING_FILES[f]) end end _li do _a 'What-If tool for analyzing Board STV votes', href: '/members/whatif' end end end _p do _ 'Note: If quorum is not achieved within 15 minutes, the online meeting may be rescheduled - be sure to submit a proxy!' _ 'Various data files about the meeting (raw-irc-log, board voting tally if present) will be checked in soon after the meeting for historical records.' _ 'Votes for the Omnibus resolution, if any, are included in raw-irc-log. We do not publish vote results for new member nominees.' end end _div id: 'after' _whimsy_panel("Timeline: After The Meeting", style: 'panel-default') do _p do _ 'Shortly after the live ASFMM Meeting ends, ' _a '@TheASF twitter', href: 'https://twitter.com/theasf' _ ' will formally announce the new board, if an Annual meeting has elected one - please wait to retweet the official announcement.' _span.text_warning 'IMPORTANT:' _ ' Do NOT publicise the names of newly elected members! In rare cases, the new candidate might not accept the honor.' end _p do _span.text_primary 'If you nominated a new member:' _ ' You ' _strong 'must' _ ' send an email with ' _a 'foundation/membership-application-email.txt', href: ASF::SVN.svnpath!('foundation', 'membership-application-email.txt') _ ' to formally invite the new member to fill out the application form; follow instructions carefully. Applications must be signed and submitted to the secretary within 30 days of the meeting to be valid.' end _p do _ "REMEMBER: newly elected members must return their application before #{mtg_timeline['member_form_date']}, otherwise they are not admitted per bylaws 4.1." end _p do _ 'You can use the following page to ' _a 'check the progress of member invitations', href: '/members/check_invitations' end end # Most/all of these links should already be included above emit_meeting(cur_mtg_dir, svn_mtg_dir, meeting_start_time, num_members, quorum_need, num_proxies, attend_irc) _div id: 'meeting-history' _whimsy_panel("Member Meeting History", style: 'panel-info') do all_mtg = Dir[File.join(MEETINGS, '19*'), File.join(MEETINGS, '2*')].sort _p do _ %{ The ASF has held #{all_mtg.count} Member's meetings in our history. Some were Annual meetings, where we elect a new board; a handful were Special mid-year meetings where we mostly just elected new Members. } _ ' Remember, member meeting minutes are ' _span.text_warning 'private' _ ' to the ASF. You can see your ' _a 'your own Attendance history at meetings.', href: '/members/inactive#attendance' _ 'Various data files and tools tracking Attendance at meetings are in ' _code 'foundation/Meetings/attend*' _ul do all_mtg.each do |mtg| _li do tmp = File.join(RECORDS_DIR, File.basename(mtg)) _a tmp, href: tmp end end end end end end end end