irc-meetings/tools/count_slot_usage.py
Thierry Carrez 90d0c47dd3 Consolidate openstack-meeting channels
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
2019-01-03 13:58:38 +01:00

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())