scripts and useful references to track contributions by users.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

get_active_wg_members.py 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2016 OpenStack Foundation
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  14. # implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. from datetime import datetime
  18. from datetime import timedelta
  19. import operator
  20. import optparse
  21. import os
  22. meeting_mappings = {
  23. 'uc': 'user_committee',
  24. 'product_team': 'product_working_group',
  25. 'large_deployments_team_monthly_meeting': 'large_deployment_team',
  26. 'ops_meetup_team': 'ops_meetups_team',
  27. 'operators_telco_nfv': 'telcowg',
  28. }
  29. def get_recent_meets(log_dir, last_active_days=180):
  30. """
  31. takes a directory heirachy that only contains meetbot
  32. txt summary files, determines the users active within
  33. the threshold. Returns a dictionary that has
  34. one entry per meeting category, containing information about
  35. who attended which meetings and how much they said.
  36. """
  37. meetings = {}
  38. now = datetime.now()
  39. active_threshold = now - timedelta(days=last_active_days)
  40. # get our list of meetings and timestamps
  41. for root, dirs, files in os.walk(log_dir):
  42. if len(files) > 0:
  43. for txt_summary in files:
  44. (meet_name, meet_date) = txt_summary.split('.', 1)
  45. meet_date = meet_date[0:-4] # drop the .txt at the end
  46. if meet_name in meeting_mappings.keys():
  47. meet_name = meeting_mappings[meet_name]
  48. meet_timestamp = datetime.strptime(meet_date, "%Y-%m-%d-%H.%M")
  49. if meet_timestamp > active_threshold:
  50. if meet_name not in meetings.keys():
  51. meetings[meet_name] = []
  52. meet_file = root + "/" + txt_summary
  53. meetings[meet_name].append(get_people_in_meeting(meet_file))
  54. return meetings
  55. def get_people_in_meeting(meeting_txt):
  56. """
  57. takes a meetbot summary file that has a section with the following format
  58. and returns a dict with username<->lines said mapping
  59. People present (lines said)
  60. ---------------------------
  61. * username (117)
  62. * username2 (50)
  63. """
  64. meeting_people = []
  65. in_people = False
  66. txt_file = open(meeting_txt)
  67. for line in txt_file:
  68. if line == "People present (lines said)\n":
  69. in_people = True
  70. elif not in_people:
  71. next
  72. elif in_people and '*' not in line:
  73. next
  74. elif in_people and 'openstack' not in line:
  75. ircnic, linessaid = line[2:-2].split('(')
  76. ircnic = ircnic.strip(" _").lower()
  77. meeting_people.append((ircnic, linessaid))
  78. txt_file.close()
  79. return meeting_people
  80. def get_meeting_aggregates(meeting_data):
  81. """
  82. Aggregates the attendance counts and lines said for users across
  83. a meeting category
  84. """
  85. meeting_aggregate = {}
  86. for meeting_name in meeting_data.keys():
  87. meeting_users = {}
  88. for meeting in meeting_data[meeting_name]:
  89. for user_tuple in meeting:
  90. if user_tuple[0] not in meeting_users.keys():
  91. meeting_users[user_tuple[0]] = {'attendance_count': 1,
  92. 'lines_said': int(user_tuple[1])}
  93. else:
  94. meeting_users[user_tuple[0]]["attendance_count"] += 1
  95. meeting_users[user_tuple[0]]["lines_said"] += int(user_tuple[1])
  96. meeting_aggregate[meeting_name] = meeting_users
  97. return meeting_aggregate
  98. def print_meet_stats(meeting_data):
  99. for meeting_name in meeting_data.keys():
  100. print "\n" + meeting_name + "\n=====================================\n"
  101. sorted_users = sorted(meeting_data[meeting_name].items(), reverse=True,
  102. key=operator.itemgetter(1))
  103. for user in sorted_users:
  104. print "{: <20} {: <20} {: <20}".format(user[0],
  105. user[1]["attendance_count"],
  106. user[1]["lines_said"])
  107. def print_eligible_usernames(meeting_data, num_meetings=1, lines_said=1, human=False):
  108. user_aggregate = {}
  109. for meeting_name in meeting_data.keys():
  110. for user_tuple in meeting_data[meeting_name].items():
  111. if user_tuple[0] not in user_aggregate.keys():
  112. user_aggregate[user_tuple[0]] = user_tuple[1]
  113. else:
  114. user_aggregate[user_tuple[0]]["lines_said"] += user_tuple[1]["lines_said"]
  115. user_aggregate[user_tuple[0]]["attendance_count"] += user_tuple[1]["attendance_count"]
  116. if human:
  117. print "\n OVERALL STATS \n=====================================\n"
  118. sorted_users = sorted(user_aggregate.items(), reverse=True,
  119. key=operator.itemgetter(1))
  120. for user in sorted_users:
  121. if user[1]["attendance_count"] >= num_meetings or user[1]["lines_said"] >= lines_said:
  122. if human:
  123. print "{: <20} {: <20} {: <20}".format(user[0],
  124. user[1]["attendance_count"],
  125. user[1]["lines_said"])
  126. else:
  127. print "{},{},{}".format(user[0],
  128. user[1]["attendance_count"],
  129. user[1]["lines_said"])
  130. def main():
  131. optparser = optparse.OptionParser()
  132. optparser.add_option(
  133. '--human', help='If set, output results in human-readable format',
  134. default=False, action="store_true")
  135. optparser.add_option(
  136. '-d', '--datadir', help='Where meeting data lives',
  137. default='./eavesdrop.openstack.org/meetings')
  138. optparser.add_option(
  139. '-t', '--days', help='Validity of attendance in days',
  140. type="int", default=183)
  141. optparser.add_option(
  142. '-n', '--nummeetings', help='Required number of meetings',
  143. type="int", default=2)
  144. optparser.add_option(
  145. '-l', '--linessaid', help='Required number of line said',
  146. type="int", default=10)
  147. options, args = optparser.parse_args()
  148. meeting_data = get_recent_meets(options.datadir, options.days)
  149. meeting_aggregate = get_meeting_aggregates(meeting_data)
  150. if options.human:
  151. print_meet_stats(meeting_aggregate)
  152. print_eligible_usernames(meeting_aggregate, options.nummeetings,
  153. options.linessaid, options.human)
  154. if __name__ == "__main__":
  155. main()