irc-meetings/tools/count_slot_usage.py

177 lines
6.4 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 = {'first-thursday': 2, 'first-friday': 2, 'weekly': 2,
'biweekly-even': 1, 'biweekly-odd': 1,
'quadweekly': 1, 'quadweekly-alternate': 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 - args.sensitivity
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')
parser.add_argument(
'--sensitivity', type=int, default=1,
help='Sensitivity of reporting. '
'Defaults to 1, which means report if no weekly slot is '
'available at the time slots considered.')
args = parser.parse_args()
return args
if '__main__' == __name__:
sys.exit(main())