| require_relative '../asf' |
| require 'tzinfo' |
| |
| module ASF |
| module Board |
| TIMEZONE = TZInfo::Timezone.get('US/Pacific') |
| |
| # sorted list of Directors |
| # default to names only |
| # if withId == true, then return hash: { id: {name: public_name}} |
| # This allows for returning additional data such as start of tenure |
| # sort is by last name |
| def self.directors(withId=false) |
| if withId |
| ASF::Service['board'].members. |
| map {|person| [person.id, {name: person.public_name}]}. |
| sort_by {|id,hash| hash[:name].split(' ').rotate}.to_h |
| else |
| ASF::Service['board'].members. |
| map {|person| person.public_name}. |
| sort_by {|name| name.split(' ').rotate} |
| end |
| end |
| |
| # list of board meeting times as listed in |
| # committers/board/calendar.txt |
| def self.calendar |
| svn = ASF::SVN['board'] |
| txt = File.read(File.join(svn, 'calendar.txt')) |
| times = txt.scan(/^\s+\*\)\s(.*)/).flatten |
| times.map {|time| TIMEZONE.local_to_utc(Time.parse(time))} |
| end |
| |
| # time of next meeting |
| def self.nextMeeting |
| time = self.calendar.select {|time| time > Time.now.utc}.min |
| |
| if not time |
| require 'chronic' |
| this_month = Time.now.strftime('%B') |
| |
| time = Chronic.parse("3rd wednesday in #{this_month}") |
| |
| if not time or time < Time.now.utc |
| time = Chronic.parse('3rd wednesday next month') |
| end |
| |
| time = TIMEZONE.local_to_utc(Time.parse("#{time.to_date} 10:30am")) |
| end |
| |
| time |
| end |
| |
| # time of previous meeting |
| def self.lastMeeting |
| next_meeting = self.nextMeeting |
| time = self.calendar.select {|time| time < next_meeting}.max |
| |
| if not time |
| require 'chronic' |
| this_month = Time.now.strftime('%B') |
| |
| time ||= Chronic.parse("3rd wednesday in #{this_month}") |
| |
| if not time or time > Time.now.utc |
| time = Chronic.parse('3rd wednesday last month') |
| end |
| end |
| |
| time |
| end |
| |
| # list of PMCs reporting in the specified meeting |
| def self.reporting(meeting) |
| month = meeting.strftime('%B') |
| ASF::Committee.load_committee_info |
| ASF::Committee.pmcs.select do |pmc| |
| pmc.report.split(', ').include? month or pmc.report == 'Every month' or |
| pmc.report.start_with? 'Next month' |
| end |
| end |
| |
| # source for shepherd information, yields a stream of director names |
| # in random order |
| class ShepherdStream < Enumerator |
| def initialize |
| @directors = ASF::Service['board'].members |
| |
| super do |generator| |
| list = [] |
| loop do |
| list = @directors.shuffle if list.empty? |
| generator.yield list.pop.public_name |
| end |
| end |
| end |
| |
| def for(pmc) |
| chair = pmc.chair |
| |
| if @directors.include? chair |
| "#{chair.public_name}" |
| else |
| "#{chair.public_name} / #{self.next.split(' ').first}" |
| end |
| end |
| end |
| |
| # Does the uid have an entry in the director intials table? |
| def self.directorHasId?(id) |
| DIRECTOR_MAP[id] |
| end |
| |
| # Return the initials for the uid |
| # Fails if there is no entry, so check first using directorHasId? |
| def self.directorInitials(id) |
| DIRECTOR_MAP[id][INITIALS] |
| end |
| |
| # Return the first name for the uid |
| # Fails if there is no entry, so check first using directorHasId? |
| def self.directorFirstName(id) |
| DIRECTOR_MAP[id][FIRST_NAME] |
| end |
| |
| # Return the display name for the uid |
| # Fails if there is no entry, so check first using directorHasId? |
| def self.directorDisplayName(id) |
| DIRECTOR_MAP[id][DISPLAY_NAME] |
| end |
| |
| private |
| |
| # Map director ids->names and ids->initials |
| # Only filled in since 2007 or so, once the preapp data in meetings is parseable |
| INITIALS = 0 |
| FIRST_NAME = 1 |
| DISPLAY_NAME = 2 |
| DIRECTOR_MAP = { |
| 'bayard' => ['hy', 'Henri', 'Henri Yandell'], |
| 'bdelacretaz' => ['bd', 'Bertrand', 'Bertrand Delacretaz'], |
| 'brett' => ['bp', 'Brett', 'Brett Porter'], |
| 'brianm' => ['bmc', 'Brian', 'Brian McCallister'], |
| 'cliffs' => ['cs', 'Cliff', 'Cliff Schmidt'], |
| 'coar' => ['kc', 'Ken', 'Ken Coar'], |
| 'curcuru' => ['sc', 'Shane', 'Shane Curcuru'], |
| 'cutting' => ['dc', 'Doug', 'Doug Cutting'], |
| 'dirkx' => ['dg', 'Dirk-Willem', 'Dirk-Willem van Gulik'], |
| 'dkulp' => ['dk', 'Daniel', 'Daniel Kulp'], |
| 'druggeri' => ['dr', 'Daniel', 'Daniel Ruggeri'], |
| 'fielding' => ['rf', 'Roy', 'Roy T. Fielding'], |
| 'geirm' => ['gmj', 'Geir', 'Geir Magnusson Jr'], |
| 'gstein' => ['gs', 'Greg', 'Greg Stein'], |
| 'isabel' => ['idf', 'Isabel', 'Isabel Drost-Fromm'], |
| 'jerenkrantz' => ['je', 'Justin', 'Justin Erenkrantz'], |
| 'jim' => ['jj', 'Jim', 'Jim Jagielski'], |
| 'ke4qqq' => ['dn', 'David', 'David Nalley'], |
| 'lrosen' => ['lr', 'Larry', 'Lawrence Rosen'], |
| 'markt' => ['mt', 'Mark', 'Mark Thomas'], |
| 'marvin' => ['mh', 'Marvin', 'Marvin Humphrey'], |
| 'mattmann' => ['cm', 'Chris', 'Chris Mattmann'], |
| 'myrle' => ['mk', 'Myrle', 'Myrle Krantz'], |
| 'noirin' => ['np', 'Noirin', 'Noirin Plunkett'], |
| 'psteitz' => ['ps', 'Phil', 'Phil Steitz'], |
| 'rbowen' => ['rb', 'Rich', 'Rich Bowen'], |
| 'rgardler' => ['rg', 'Ross', 'Ross Gardler'], |
| 'rubys' => ['sr', 'Sam', 'Sam Ruby'], |
| 'rvs' => ['rs', 'Roman', 'Roman Shaposhnik'], |
| 'striker' => ['ss', 'Sander', 'Sander Striker'], |
| 'tdunning' => ['td', 'Ted', 'Ted Dunning'], |
| 'wave' => ['df', 'Dave', 'Dave Fisher'], |
| 'wohali' => ['jt', 'Joan', 'Joan Touzet'], |
| } |
| |
| end |
| end |