
With some IRC meetings having moved to team channels, we have a lot more room in the "common" meeting rooms. To the point where #openstack-meeting-cp and #openstack-meeting-5 are taking up valuable bot space (bots can only join so many channels) for very little usage. Furthermore, the benefit of using common channels is that the team activity is publicized to meeting channel lurkers, and neither #openstack-meeting-cp not #openstack-meeting-5 gathered a faithful attendance. This propose to consolidate meetings to the other meeting channels (#openstack-meeting{,-alt,3,4}) by moving the only two meetings that actually took advantage of those two meeting rooms. Once this merges, we can start the work of removing those from the meeting bot watch. Change-Id: Iafcba92f908f4305492737ffea2771acf85a7fbc
170 lines
6.0 KiB
Python
Executable File
170 lines
6.0 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import calendar
|
|
import csv
|
|
import datetime
|
|
import locale
|
|
import os
|
|
import sys
|
|
|
|
import pytz
|
|
import yaml
|
|
|
|
# Ensure calendar.day_name gives us Monday, Tuesday, ...
|
|
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
|
|
|
|
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))
|
|
EAVESDROP = 'eavesdrop.openstack.org'
|
|
MEETINGS_PATH = os.path.join(BASE_DIR, 'meetings')
|
|
WEEKDAYS = list(calendar.day_name)
|
|
WEEK_COUNTS = {'weekly': 2, 'biweekly-even': 1, 'biweekly-odd': 1, 'adhoc': 0}
|
|
CHANNELS = ['openstack-meeting', 'openstack-meeting-alt',
|
|
'openstack-meeting-3', 'openstack-meeting-4']
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
meetings = read_meetings(MEETINGS_PATH)
|
|
|
|
meeting_counts = calculate_meeting_counts(meetings)
|
|
|
|
print("Day\tUTC Hour")
|
|
available_slots = 2 * len(CHANNELS)
|
|
full_time_slot = available_slots - 1
|
|
for day in WEEKDAYS:
|
|
for hour in range(24):
|
|
slot_usage = len(meeting_counts[hour][day])
|
|
if slot_usage >= full_time_slot:
|
|
print('{:<10} {}:00 {:<2} out of {} slots full'.format(
|
|
day, hour, slot_usage, available_slots))
|
|
# Handy for debugging
|
|
# print("\t{}".format(
|
|
# "\n\t".join(sorted(meeting_counts[hour][day]))))
|
|
slot_meetings = sorted(set(meeting_counts[hour][day]))
|
|
for meeting_info in slot_meetings:
|
|
print('{:<4}{}'.format('', meeting_info))
|
|
|
|
if args.csv:
|
|
print()
|
|
write_csv_file(args.csv, meeting_counts)
|
|
|
|
|
|
def calculate_meeting_counts(meetings):
|
|
now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
|
|
meeting_counts = {}
|
|
for hour in range(24):
|
|
meeting_counts[hour] = {k: [] for k in WEEKDAYS}
|
|
|
|
for meeting in meetings:
|
|
if 'meeting_id' in meeting:
|
|
meeting_id = ('http://{}/meetings/{}/{:4d}/?C=N;O=D'.format(
|
|
EAVESDROP, meeting['meeting_id'].replace('-', '_'), now.year))
|
|
else:
|
|
meeting_id = meeting['filefrom']
|
|
|
|
for schedule in meeting['schedule']:
|
|
try:
|
|
day = schedule['day']
|
|
time = schedule['time']
|
|
frequency = schedule['frequency']
|
|
week_count = WEEK_COUNTS[frequency]
|
|
irc = schedule['irc']
|
|
except KeyError:
|
|
print("KeyError in here somewhere!")
|
|
print("meeting = {}".format(meeting))
|
|
print("schedule = {}".format(schedule))
|
|
print("\n")
|
|
continue
|
|
|
|
hour = int(time[:-2])
|
|
mins = int(time[-2:])
|
|
duration = int(schedule.get('duration', 60))
|
|
if duration > 60:
|
|
print("Meeting longer than 60 minutes. We don't understand "
|
|
"that yet")
|
|
print("meeting = {}".format(meeting))
|
|
print("schedule = {}".format(schedule))
|
|
print("\n")
|
|
|
|
if irc not in CHANNELS:
|
|
# Handy for debugging
|
|
# print("{}: {}".format(meeting['filefrom'], schedule))
|
|
continue
|
|
|
|
meeting_info = (
|
|
"{:<13} - {}/{} - {:<21} - {}".format(
|
|
frequency, time, duration, irc, meeting_id))
|
|
|
|
# This is a little hacky way to handle alternating meetings. The
|
|
# "counts" gathered are per fortnight so a weekly meeting takes up
|
|
# 2 slots, and an alternating (biweekly-*) only one. This means
|
|
# that a "full slot" will be one that has 8 (2 * number of meeting
|
|
# channels) scheduled meetings
|
|
for i in range(week_count):
|
|
meeting_counts[hour][day].append(meeting_info)
|
|
# Check for and record meetings that cross multiple hours (This
|
|
# assumes that we don't have any meetings that are longer than
|
|
# 60mins)
|
|
if (mins + duration) > 60:
|
|
meeting_counts[(hour+1) % 24][day].append(meeting_info)
|
|
return meeting_counts
|
|
|
|
|
|
def write_csv_file(filename, meeting_counts):
|
|
filename = os.path.abspath(os.path.expanduser(filename))
|
|
with open(filename, 'w') as out_file:
|
|
writer = csv.writer(out_file)
|
|
writer.writerow(["Hour"] + WEEKDAYS)
|
|
for hour in range(24):
|
|
row = [hour] + [len(meeting_counts[hour][day]) for day in WEEKDAYS]
|
|
writer.writerow(row)
|
|
print("Created CSV file of meeting slot usage at: {}".format(filename))
|
|
|
|
|
|
def read_meetings(meeting_directory):
|
|
meetings = []
|
|
if not os.path.isdir(meeting_directory):
|
|
sys.exit("Unable to find meeting directory: {}".format(
|
|
meeting_directory))
|
|
|
|
for dirpath, dirnames, filenames in os.walk(meeting_directory):
|
|
for file_name in filenames:
|
|
if not file_name.endswith('.yaml'):
|
|
continue
|
|
full_path = os.path.join(dirpath, file_name)
|
|
with open(full_path, 'r') as f_obj:
|
|
obj = yaml.safe_load(f_obj)
|
|
obj['filefrom'] = full_path
|
|
meetings.append(obj)
|
|
return meetings
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(
|
|
description='Check meeting count time usage')
|
|
parser.add_argument(
|
|
'--csv', metavar='FILE_NAME',
|
|
help='If specified, write counts to the specified CSV file')
|
|
|
|
args = parser.parse_args()
|
|
return args
|
|
|
|
|
|
if '__main__' == __name__:
|
|
sys.exit(main())
|