blob: 6930939fb0f7b17c421dde59a6579f15e416b512 [file] [log] [blame]
#!/usr/bin/env ruby
PAGETITLE = "Active Members not participating in meetings" # Wvisible:meeting
$LOAD_PATH.unshift '/srv/whimsy/lib'
require 'whimsy/asf'
require 'wunderbar/bootstrap'
require 'date'
require 'json'
require 'wunderbar/jquery/stupidtable'
# separator / is added when link is generated
ROSTER = "/roster/committer"
# locate and read the attendance file
MEETINGS = ASF::SVN['Meetings']
attendance = JSON.parse(IO.read(File.join(MEETINGS, 'attendance.json')))
# extract and format dates
dates = attendance['dates'].sort.
map {|date| Date.parse(date).strftime('%Y-%b')}
# compute mappings of names to ids
members = ASF::Member.list
active = Hash[members.select {|id, data| not data['status']}]
nameMap = Hash[members.map {|id, data| [id, data[:name]]}]
idMap = Hash[nameMap.to_a.map(&:reverse)]
# analyze attendance
matrix = attendance['matrix'].map do |name, meetings|
id = idMap[name]
next unless id and active[id]
data = meetings.sort.reverse.map(&:last)
first = data.length
missed = (data.index {|datum| datum != '-'} || data.length)
[id, name, first, missed]
end
# produce HTML
_html do
_body? do
_whimsy_body(
title: PAGETITLE,
subtitle: 'Select A Date:',
related: {
'/members/attendance-xcheck' => 'Members Meeting Attendance Crosscheck',
'/members/inactive' => 'Inactive Member Feedback Form',
'/members/proxy' => 'Members Meeting Proxy Assignment',
'/members/subscriptions' => 'Members@ Mailing List Crosscheck'
},
helpblock: -> {
_form_ do
_span "List of members that have not participated, starting with the "
_select name: 'meetingsMissed', onChange: 'this.form.submit()' do
dates.reverse.each_with_index do |name, i|
_option name, value: i+1, selected: (i+1 == @meetingsMissed.to_i)
end
end
_span "meeting. Active members does not include emeritus or deceased members. Includes data thru #{attendance['dates'].last} meeting."
end
_h4 'Definitions'
_p do
_ 'Participating is defined by doing at least one of the following:'
_ul do
_li 'Attending a members meeting in IRC'
_li 'Voting in an election'
_li 'Assigning a proxy'
end
end
}
) do
@meetingsMissed = (@meetingsMissed || 3).to_i
count = 0
_table.table.table_hover do
_thead do
_tr do
_th 'Name', data_sort: 'string'
_th 'Membership start date', data_sort: 'string'
_th 'Last participated', data_sort: 'string'
end
end
matrix.each do |id, name, first, missed|
next unless id
if missed >= @meetingsMissed
_tr_ do
_td! {_a nameMap[id], href: "#{ROSTER}/#{id}"}
_td dates[-first-1] || dates.first
if missed >= first
_td {_em 'never'}
else
_td dates[-missed-1]
end
end
count += 1
end
end
end
_div.count "Count: #{count} members inactive for #{@meetingsMissed} meetings."
_script %{
var table = $(".table").stupidtable();
table.on("aftertablesort", function (event, data) {
var th = $(this).find("th");
th.find(".arrow").remove();
var dir = $.fn.stupidtable.dir;
var arrow = data.direction === dir.ASC ? "↑" : "↓";
th.eq(data.column).append('<span class="arrow">' + arrow +'</span>');
});
}
end
end
end
_json do
meetingsMissed = (@meetingsMissed || 3).to_i
inactive = matrix.select do |id, name, first, missed|
id and missed >= meetingsMissed
end
Hash[inactive.map {|id, name, first, missed|
[id, {name: name, missed: missed, status: 'no response yet'}]
}]
end