#!/usr/bin/env python # # Copyright (c) 2016 OpenStack Foundation # # 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 datetime import datetime from datetime import timedelta import operator import optparse import os meeting_mappings = { 'uc': 'user_committee', 'product_team': 'product_working_group', 'large_deployments_team_monthly_meeting': 'large_deployment_team', 'ops_meetup_team': 'ops_meetups_team', 'operators_telco_nfv': 'telcowg', } def get_recent_meets(log_dir, last_active_days=180): """ takes a directory heirachy that only contains meetbot txt summary files, determines the users active within the threshold. Returns a dictionary that has one entry per meeting category, containing information about who attended which meetings and how much they said. """ meetings = {} now = datetime.now() active_threshold = now - timedelta(days=last_active_days) # get our list of meetings and timestamps for root, dirs, files in os.walk(log_dir): if len(files) > 0: for txt_summary in files: (meet_name, meet_date) = txt_summary.split('.', 1) meet_date = meet_date[0:-4] # drop the .txt at the end if meet_name in meeting_mappings.keys(): meet_name = meeting_mappings[meet_name] meet_timestamp = datetime.strptime(meet_date, "%Y-%m-%d-%H.%M") if meet_timestamp > active_threshold: if meet_name not in meetings.keys(): meetings[meet_name] = [] meet_file = root + "/" + txt_summary meetings[meet_name].append(get_people_in_meeting(meet_file)) return meetings def get_people_in_meeting(meeting_txt): """ takes a meetbot summary file that has a section with the following format and returns a dict with username<->lines said mapping People present (lines said) --------------------------- * username (117) * username2 (50) """ meeting_people = [] in_people = False txt_file = open(meeting_txt) for line in txt_file: if line == "People present (lines said)\n": in_people = True elif not in_people: next elif in_people and '*' not in line: next elif in_people and 'openstack' not in line: ircnic, linessaid = line[2:-2].split('(') ircnic = ircnic.strip(" _").lower() meeting_people.append((ircnic, linessaid)) txt_file.close() return meeting_people def get_meeting_aggregates(meeting_data): """ Aggregates the attendance counts and lines said for users across a meeting category """ meeting_aggregate = {} for meeting_name in meeting_data.keys(): meeting_users = {} for meeting in meeting_data[meeting_name]: for user_tuple in meeting: if user_tuple[0] not in meeting_users.keys(): meeting_users[user_tuple[0]] = {'attendance_count': 1, 'lines_said': int(user_tuple[1])} else: meeting_users[user_tuple[0]]["attendance_count"] += 1 meeting_users[user_tuple[0]]["lines_said"] += int(user_tuple[1]) meeting_aggregate[meeting_name] = meeting_users return meeting_aggregate def print_meet_stats(meeting_data): for meeting_name in meeting_data.keys(): print "\n" + meeting_name + "\n=====================================\n" sorted_users = sorted(meeting_data[meeting_name].items(), reverse=True, key=operator.itemgetter(1)) for user in sorted_users: print "{: <20} {: <20} {: <20}".format(user[0], user[1]["attendance_count"], user[1]["lines_said"]) def print_eligible_usernames(meeting_data, num_meetings=1, lines_said=1, human=False): user_aggregate = {} for meeting_name in meeting_data.keys(): for user_tuple in meeting_data[meeting_name].items(): if user_tuple[0] not in user_aggregate.keys(): user_aggregate[user_tuple[0]] = user_tuple[1] else: user_aggregate[user_tuple[0]]["lines_said"] += user_tuple[1]["lines_said"] user_aggregate[user_tuple[0]]["attendance_count"] += user_tuple[1]["attendance_count"] if human: print "\n OVERALL STATS \n=====================================\n" sorted_users = sorted(user_aggregate.items(), reverse=True, key=operator.itemgetter(1)) for user in sorted_users: if user[1]["attendance_count"] >= num_meetings or user[1]["lines_said"] >= lines_said: if human: print "{: <20} {: <20} {: <20}".format(user[0], user[1]["attendance_count"], user[1]["lines_said"]) else: print "{},{},{}".format(user[0], user[1]["attendance_count"], user[1]["lines_said"]) def main(): optparser = optparse.OptionParser() optparser.add_option( '--human', help='If set, output results in human-readable format', default=False, action="store_true") optparser.add_option( '-d', '--datadir', help='Where meeting data lives', default='./eavesdrop.openstack.org/meetings') optparser.add_option( '-t', '--days', help='Validity of attendance in days', type="int", default=183) optparser.add_option( '-n', '--nummeetings', help='Required number of meetings', type="int", default=2) optparser.add_option( '-l', '--linessaid', help='Required number of line said', type="int", default=10) options, args = optparser.parse_args() meeting_data = get_recent_meets(options.datadir, options.days) meeting_aggregate = get_meeting_aggregates(meeting_data) if options.human: print_meet_stats(meeting_aggregate) print_eligible_usernames(meeting_aggregate, options.nummeetings, options.linessaid, options.human) if __name__ == "__main__": main()