Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distinguish directors and guests in BoD attendance lists #61

Merged
merged 5 commits into from
Sep 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 17 additions & 23 deletions staff/meetings/bod
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,33 @@ import argparse
import sys
from os.path import join

import bod
import meetings


def get_last_meeting():
"""Returns a (semester, filename) pair for the most recent meeting."""
if not hasattr(get_last_meeting, 'cached_val'):
meeting_lst = meetings.get_minutes(bod.get_bod_minutes_path())
get_last_meeting.cached_val = (meetings.get_semester(), meeting_lst[-1])
return get_last_meeting.cached_val


def quorum(args):
print(meetings.quorum())
print(bod.quorum(*get_last_meeting()))


def ls(args):
pairs = meetings.ls(args.state).items()
pairs = sorted(pairs, key=meetings.membership_key, reverse=True)
for user, status in pairs:
# max acct length is 16
print('{:16} {}'.format(user, status))
for director in sorted(bod.ls(*get_last_meeting())):
print(director)


def danger(args):
path = meetings.get_bod_minutes_path()
minutes = meetings.get_minutes(path)
if len(minutes) > 0:
bod = set(meetings.ls(state='bod'))
attended_last_meeting = set(meetings.attendance(join(path, minutes[-1])))
for user in bod - attended_last_meeting:
print(user)
on_bod = bod.ls(*get_last_meeting())
at_last_meeting = set(bod.get_attending_directors(*get_last_meeting()))


def update(args):
"""update membership file based on minutes in current semester"""
meetings.update_membership()
for director in sorted(on_bod - at_last_meeting):
print(director)


if __name__ == '__main__':
Expand All @@ -40,19 +39,14 @@ if __name__ == '__main__':
# lists people in "danger" of being kicked off
# i.e. people who missed last meeting on bod
'danger': danger,
'update': update,
}

parser = argparse.ArgumentParser(description='View bod composition and information')
subparser = parser.add_subparsers(dest='command', help='command to run')
subparser_danger = subparser.add_parser('danger', help='users in danger of being kicked off bod')
subparser_update = subparser.add_parser('update', help='update membership file')
subparser_ls = subparser.add_parser('ls', help='list users and their bod eligibility state')
subparser_quorum = subparser.add_parser('quorum', help='current bod quorum')
subparser_ls.add_argument('state', nargs='?', default='all',
choices=['all', 'bod', 'offbod'],
help='group of users to list'
)

if len(sys.argv) == 1:
parser.print_help()
else:
Expand Down
183 changes: 183 additions & 0 deletions staff/meetings/bod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
"""Module for BoD meeting logic."""
from math import ceil
from os.path import join

import meetings


def quorum(semester, minutes_filename):
"""Returns the quorum for the given BoD meeting.

Args:
semester: the directory name (as given by ``meetings.get_semester()``)
for the semester in which the meeting took place
minutes_filename: the filename of the file containing the minutes for
the meeting

"""
bod = ls(*meetings.get_prev_meeting('bod', semester, minutes_filename))
return int(ceil(2 / 3 * len(bod)))


def get_bod_minutes_path(semester=meetings.get_semester()):
"""Gets the path to the BoD minutes directory for the given semester."""
return meetings.get_minutes_path('bod', semester=semester)


def get_attending_directors(semester, minutes_filename):
"""Returns a list of directors present at the given meeting.

Args:
semester: the directory name (as given by ``meetings.get_semester()``)
for the semester in which the meeting took place
minutes_filename: the filename of the file containing the minutes for
the meeting

"""
minutes_file = join(get_bod_minutes_path(semester=semester),
minutes_filename)
attendees = []
with open(minutes_file, 'r') as fin:
# Skip everything before the line "Directors in attendance:"
for line in fin:
if line == 'Directors in attendance:\n':
break

for line in fin:
if line.strip():
attendees.append(line.strip())
else:
break

assert attendees, 'A BoD meeting needs at least one director present'
return attendees


def get_attending_guests(semester, minutes_filename):
"""Returns lists of guests present at the given meeting.

Args:
semester: the directory name (as given by ``meetings.get_semester()``)
for the semester in which the meeting took place
minutes_filename: the filename of the file containing the minutes for
the meeting

Returns:
A tuple of two lists. The first list is of guests appointed to BoD at
that meeting, the second list is of all other guests.

"""
minutes_file = join(get_bod_minutes_path(semester=semester),
minutes_filename)
joined = []
visiting = []
with open(minutes_file, 'r') as fin:
# Skip everything before the line "Guests in attendance:"
for line in fin:
if line == 'Guests in attendance:\n':
break

for line in fin:
line = line.strip()
if line:
if line.endswith('*'):
joined.append(line[:-1])
else:
visiting.append(line)
else:
break

return (joined, visiting)


def ls(semester, minutes_filename):
"""Lists everyone on BoD at the conclusion of the given meeting.

Args:
semester: the directory name (as given by ``meetings.get_semester()``)
for the semester in which the meeting took place
minutes_filename: the filename of the file containing the minutes for
the meeting

Returns:
A set of BoD members as specified above

"""
prev_sem, prev_fname = meetings.get_prev_meeting('bod', semester,
minutes_filename)

# FIXME: Always include the GM and SM on this list
# (And the ASUC EVP and CFO if we want to be pedantic)
prev_on_bod = set(get_attending_directors(prev_sem, prev_fname)
+ get_attending_guests(prev_sem, prev_fname)[0])
cur_on_bod = set(get_attending_directors(semester, minutes_filename)
+ get_attending_guests(semester, minutes_filename)[0])
return prev_on_bod | cur_on_bod


def split_attendance(semester, minutes_filename):
"""Splits the attendance section for the given meeting's minutes.

Splits the attendance section into a section for BoD members, a
section for guests joining BoD, and a section for guests not joining
BoD.

Args:
minutes_filename: the file containing the minutes for the meeting

Returns:
A tuple of two lists. The first list is of guests appointed to BoD at
that meeting, the second list is of all other guests.

"""
minutes_file = join(get_bod_minutes_path(semester=semester),
minutes_filename)
with open(minutes_file, 'r') as fin:
lines = fin.readlines()

attendance_start = lines.index('Attendance:\n')
attendance_end = attendance_start + 1
while (attendance_end < len(lines)
and lines[attendance_end].strip()):
attendance_end += 1
attendees = {l.strip()
for l in lines[attendance_start + 1:attendance_end]}

replacement_lines = []

replacement_lines.append('Directors in attendance:\n')
on_bod = set(ls(*meetings.get_prev_meeting('bod', semester,
minutes_filename)))
for director in sorted(attendees & on_bod):
replacement_lines.append(director + '\n')
replacement_lines.append('\n')

guests = attendees - on_bod
appointed_guests = []
visiting_guests = []
for guest in sorted(guests):
reply = None
while reply not in {'y', 'yes', 'n', 'no'}:
reply = input(guest + ' is not on BoD. Would they like to join? '
'(y/n) ')
if reply == 'y' or reply == 'yes':
appointed_guests.append(guest)
else:
visiting_guests.append(guest)

if appointed_guests or visiting_guests:
replacement_lines.append('Guests in attendance:\n')
for guest in appointed_guests:
replacement_lines.append(guest + '*\n')
for guest in visiting_guests:
replacement_lines.append(guest + '\n')
replacement_lines.append('\n')
replacement_lines.append('Guests marked with a * were appointed to '
'BoD.\n')

# Replace attendance block in lines with the replacement lines
lines[attendance_start:attendance_end] = replacement_lines

with open(minutes_file, 'w') as fout:
for line in lines:
fout.write(line)
Loading