Retire uc-recognition repo
As discussed in TC meeting[1], TC is retiring the uc-recognition repo. [1] https://meetings.opendev.org/meetings/tc/2021/tc.2021-06-17-15.00.log.html#l-98 Change-Id: Ie4df1226ad07bad2f6a549b7e31c79f5b896470e
This commit is contained in:
parent
3a4f57de71
commit
8d9e110d9c
201
LICENSE
201
LICENSE
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
10
README.md
10
README.md
@ -1,10 +0,0 @@
|
||||
# uc-recognition
|
||||
This repository contains scripts and useful references to track contributions to OpenStack by users
|
||||
|
||||
Find active moderators on Ask OpenStack:
|
||||
* get_active_moderator.py
|
||||
|
||||
Uses IRC logs to attempt to determine active working group members:
|
||||
* get_meeting_data.sh
|
||||
* get_active_wg_members.py
|
||||
|
10
README.rst
Normal file
10
README.rst
Normal file
@ -0,0 +1,10 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For any further questions, please email
|
||||
openstack-discuss@lists.openstack.org or join #openstack-dev on
|
||||
OFTC.
|
@ -1,161 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (C) 2013-2014 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.
|
||||
#
|
||||
# Soren Hansen wrote the original version of this script.
|
||||
# James Blair hacked it up to include email addresses from gerrit.
|
||||
# Jeremy Stanley overhauled it for gerrit 2.8 and our governance repo.
|
||||
# Tom Fifield cut it to pieces as a quick hack for the UC recognition to be
|
||||
# replaced with something nicer at the earliest possible time
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import optparse
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import io
|
||||
|
||||
import paramiko
|
||||
|
||||
MAILTO_RE = re.compile('mailto:(.*)')
|
||||
USERNAME_RE = re.compile('username:(.*)')
|
||||
|
||||
class Account(object):
|
||||
def __init__(self, num):
|
||||
self.num = num
|
||||
self.full_name = ''
|
||||
self.emails = []
|
||||
self.username = None
|
||||
|
||||
|
||||
def get_account(accounts, num):
|
||||
a = accounts.get(num)
|
||||
if not a:
|
||||
a = Account(num)
|
||||
accounts[num] = a
|
||||
return a
|
||||
|
||||
|
||||
def repo_stats(repo, output, begin, end, keyfile, user):
|
||||
username_accounts = {}
|
||||
atcs = []
|
||||
|
||||
QUERY = "project:%s status:merged" % repo
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.load_system_host_keys()
|
||||
client.connect(
|
||||
'review.openstack.org', port=29418,
|
||||
key_filename=os.path.expanduser(keyfile), username=user)
|
||||
stdin, stdout, stderr = client.exec_command(
|
||||
'gerrit query %s --all-approvals --format JSON' % QUERY)
|
||||
|
||||
done = False
|
||||
last_sortkey = ''
|
||||
begin_time = datetime.datetime(
|
||||
int(begin[0:4]), int(begin[4:6]), int(begin[6:8]),
|
||||
int(begin[8:10]), int(begin[10:12]), int(begin[12:14]))
|
||||
end_time = datetime.datetime(
|
||||
int(end[0:4]), int(end[4:6]), int(end[6:8]),
|
||||
int(end[8:10]), int(end[10:12]), int(end[12:14]))
|
||||
|
||||
count = 0
|
||||
earliest = datetime.datetime.now()
|
||||
while not done:
|
||||
for l in stdout:
|
||||
data = json.loads(l)
|
||||
if 'rowCount' in data:
|
||||
if data['rowCount'] < 500:
|
||||
done = True
|
||||
continue
|
||||
count += 1
|
||||
if 'sortKey' in data.keys():
|
||||
last_sortkey = data['sortKey']
|
||||
if 'owner' not in data:
|
||||
continue
|
||||
if 'username' not in data['owner']:
|
||||
continue
|
||||
account = Account(None)
|
||||
account.username = data['owner']['username']
|
||||
account.emails = [data['owner']['email']]
|
||||
account.full_name = data['owner']['name']
|
||||
approved = False
|
||||
for ps in data['patchSets']:
|
||||
if 'approvals' not in ps:
|
||||
continue
|
||||
for aprv in ps['approvals']:
|
||||
if aprv['type'] != 'SUBM':
|
||||
continue
|
||||
ts = datetime.datetime.fromtimestamp(aprv['grantedOn'])
|
||||
if ts < begin_time or ts > end_time:
|
||||
continue
|
||||
approved = True
|
||||
if ts < earliest:
|
||||
earliest = ts
|
||||
if approved and account not in atcs:
|
||||
atcs.append(account)
|
||||
if not done:
|
||||
stdin, stdout, stderr = client.exec_command(
|
||||
'gerrit query %s resume_sortkey:%s --all-approvals'
|
||||
' --format JSON' % (QUERY, last_sortkey))
|
||||
|
||||
print 'repo: %s' % repo
|
||||
print 'examined %s changes' % count
|
||||
print 'earliest timestamp: %s' % earliest
|
||||
output_file = io.open(output, 'w', encoding='UTF-8')
|
||||
for a in atcs:
|
||||
output_file.write(a.username + ","+ a.full_name + "," + a.emails[0] + "\n")
|
||||
output_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
now = ''.join(
|
||||
'%02d' % x for x in datetime.datetime.utcnow().utctimetuple()[:6])
|
||||
|
||||
optparser = optparse.OptionParser()
|
||||
optparser.add_option(
|
||||
'-b', '--begin', help='begin date/time (e.g. 20131017000000)')
|
||||
optparser.add_option(
|
||||
'-e', '--end', default=now, help='end date/time (default is now)')
|
||||
optparser.add_option(
|
||||
'-k', '--keyfile', default='~/.ssh/id_rsa',
|
||||
help='SSH key (default is ~/.ssh/id_rsa)')
|
||||
optparser.add_option(
|
||||
'-u', '--user', default=os.environ['USER'],
|
||||
help='SSH username (default is $USER)')
|
||||
options, args = optparser.parse_args()
|
||||
|
||||
projects = ['openstack/ops-tags-team',
|
||||
'openstack/osops-tools-monitoring',
|
||||
'openstack/osops-tools-generic',
|
||||
'openstack/osops-example-configs',
|
||||
'openstack/osops-tools-logging',
|
||||
'openstack/osops-tools-contrib',
|
||||
'openstack/openstack-user-stories',
|
||||
'openstack/uc-recognition',
|
||||
'openstack/osops-coda']
|
||||
|
||||
for repo in projects:
|
||||
output = '%s.csv' % repo.split('/')[-1]
|
||||
repo_stats(repo, output, options.begin, options.end,
|
||||
options.keyfile, options.user)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,80 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import requests
|
||||
import time
|
||||
|
||||
user_list = 'https://ask.openstack.org/en/api/v1/users/'
|
||||
|
||||
params = dict(
|
||||
sort='reputation',
|
||||
page=1
|
||||
)
|
||||
|
||||
|
||||
def get_user_data(karma_level):
|
||||
"""
|
||||
Loop through the user list to find users that have greater karma than
|
||||
karma level.
|
||||
Returns a list of user data dicts.
|
||||
"""
|
||||
page = 1
|
||||
session = requests.Session()
|
||||
response = session.get(user_list, params=params)
|
||||
user_data = json.loads(response.text)['users']
|
||||
while user_data[-1]['reputation'] >= karma_level:
|
||||
page = page + 1
|
||||
params.update({'page': page})
|
||||
print "Getting page: %d" % page
|
||||
response = session.get(user_list, params=params)
|
||||
user_data.extend(json.loads(response.text)['users'])
|
||||
time.sleep(3)
|
||||
|
||||
# since pages are big chunks, we will have some users that are
|
||||
# having karma lower than karma_level in the last page. Remove them.
|
||||
while user_data[-1]['reputation'] < karma_level:
|
||||
user_data.pop()
|
||||
|
||||
return user_data
|
||||
|
||||
|
||||
def get_active_users(user_data, last_active_days=180):
|
||||
"""
|
||||
Give a list of user dict objects, return the ones that
|
||||
were active within the number of days specificed by
|
||||
last_active days.
|
||||
Prints a list of usernames, reputations and IDs
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
active_threshold = now - datetime.timedelta(days=last_active_days)
|
||||
for user in user_data:
|
||||
last_seen_at = datetime.datetime.fromtimestamp(
|
||||
int(user['last_seen_at']))
|
||||
if last_seen_at > active_threshold:
|
||||
print "{: <20} {: <20}".format(user['username'], str(user['id']))
|
||||
|
||||
|
||||
def main():
|
||||
user_data = get_user_data(karma_level=200)
|
||||
get_active_users(user_data, last_active_days=180)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,176 +0,0 @@
|
||||
#!/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()
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Downloads the TXT summary file from the meetbot records
|
||||
# at OpenStack for specific meetings
|
||||
|
||||
MEETINGS="operators_ops_tools_monitoring ops_tags _operator_tags"
|
||||
MEETINGS="$MEETINGS large_deployment_team large_deployments_team"
|
||||
MEETINGS="$MEETINGS large_deployments_team_monthly_meeting"
|
||||
MEETINGS="$MEETINGS log_wg openstack_operators ops_meetups_team ops_meetup_team"
|
||||
MEETINGS="$MEETINGS product_team product_work_group product_working_group"
|
||||
MEETINGS="$MEETINGS scientific_wg telcowg uc user_committee wos_mentoring"
|
||||
MEETINGS="$MEETINGS massively_distributed_clouds operators_telco_nfv"
|
||||
|
||||
for meeting in $MEETINGS
|
||||
do
|
||||
wget --no-parent --recursive --accept "*.txt" --reject="*.log.txt" http://eavesdrop.openstack.org/meetings/$meeting/
|
||||
done
|
Loading…
Reference in New Issue
Block a user