Skip to content

Commit 9c0b76e

Browse files
committed
Add scripts for generating developer metrics about EdgeX
Signed-off-by: Michael Hall <[email protected]>
1 parent 0454aa7 commit 9c0b76e

File tree

4 files changed

+258
-0
lines changed

4 files changed

+258
-0
lines changed

project-metrics/README.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
## Fetch repos
3+
4+
Scripts that generate Git statistics use a local checkout of EdgeX git repos. Before you run those scripts, you will need to fetch the latest contents from Github. To do that, run:
5+
6+
```
7+
./retch_repos.sh
8+
```
9+
10+
This will create a local ./repos folder with a checkout of all the EdgeX repositories from Github.
11+
12+
## Release Statistics
13+
14+
The Release Notes document should include Git and Github statistics. To create those run:
15+
16+
```
17+
./release_metrics.py <YYYY-MM> <YYYY-MM> ./repos/*
18+
```
19+
20+
Where the first YYYY-MM is the beginning month of the release cycle, and the second YYYY-MM is the ending month.
21+
22+
(Tip: To generate statistics for just one repo, specify it as the last parameter instead of ./repos/*)
23+
24+
(Tip: If you run this more than once, you'll need to get an access token from Github and set it to the environment variable GITHUB_ACCESS_TOKEN)
25+
26+
## Dockerhub stats
27+
28+
This script will report download numbers for every Docker image in the edgexfoundry organization:
29+
30+
```
31+
./dockerhub-downloads.py
32+
```
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/python3
2+
3+
import requests
4+
5+
LIST_REPOS = 'https://hub.docker.com/v2/repositories/edgexfoundry/'
6+
GET_STATS = 'https://hub.docker.com/v2/repositories/%(namespace)s/%(name)s/'
7+
8+
counts = dict()
9+
col_width = 0;
10+
11+
next_page = LIST_REPOS
12+
while next_page is not None:
13+
resp = requests.get(next_page)
14+
next_page = None
15+
if resp.status_code == 200:
16+
data = resp.json()
17+
# Read project data
18+
for repo in data['results']:
19+
counts[repo['name']] = repo['pull_count']
20+
if len(repo['name']) > col_width:
21+
col_width = len(repo['name'])
22+
23+
if data['next'] is not None:
24+
next_page = data['next']
25+
26+
total = 0
27+
header = "{0:<%d} : {1}" % col_width
28+
row = "{0:<%d} : {1:>9,}" % col_width
29+
print()
30+
print(header.format('Image', 'Downloads'))
31+
print('-'*(col_width+1) + ':' + '-'*10)
32+
for name, count in counts.items():
33+
print(row.format(name, count))
34+
total += count
35+
print('-'*(col_width+1) + ':' + '-'*10)
36+
print(row.format('Total', total))
37+

project-metrics/fetch_repos.sh

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
repos=(ci-management
2+
core-command
3+
core-command-client
4+
core-config-seed
5+
core-config-watcher
6+
core-data
7+
core-data-client
8+
core-domain
9+
core-exception
10+
core-metadata
11+
core-metadata-client
12+
core-test
13+
developer-scripts
14+
device-sdk
15+
device-sdk-tools
16+
device-virtual
17+
docker-core-command
18+
docker-core-config-seed
19+
docker-core-consul
20+
docker-core-data
21+
docker-core-metadata
22+
docker-device-virtual
23+
docker-edgex-mongo
24+
docker-edgex-mongo-seed
25+
docker-edgex-volume
26+
docker-export-client
27+
docker-export-distro
28+
docker-support-logging
29+
docker-support-notifications
30+
docker-support-rulesengine
31+
docker-support-scheduler
32+
export-client
33+
export-distro
34+
export-domain
35+
export-test
36+
edgex-ui-go
37+
support-domain
38+
support-logging
39+
support-logging-client
40+
support-notifications
41+
support-notifications-client
42+
support-rulesengine
43+
support-scheduler
44+
device-bacnet
45+
device-bluetooth
46+
device-fischertechnik
47+
device-modbus
48+
device-mqtt
49+
device-snmp
50+
device-domain
51+
device-controller
52+
device-sdk-go
53+
device-sdk-c
54+
blackbox-testing
55+
export-go
56+
consul-client-go
57+
core-clients-go
58+
core-command-go
59+
core-data-go
60+
core-domain-go
61+
core-metadata-go
62+
support-logging-client-go
63+
support-notifications-client-go
64+
support-domain-go
65+
device-scheduling
66+
edgex-go
67+
core-config-seed-go
68+
security-api-gateway
69+
security-secret-store)
70+
71+
72+
for repo in "${repos[@]}"
73+
do
74+
#echo $repo
75+
if [ ! -d ./repos/$repo ]; then
76+
echo "Cloning $repo from https://github.com/edgexfoundry/$repo"
77+
git clone https://github.com/edgexfoundry/$repo ./repos/$repo
78+
else
79+
cd ./repos/$repo
80+
echo "Updating $repo"
81+
git checkout master && git fetch --all && git pull
82+
cd ../..
83+
fi;
84+
85+
### Uncomment/comment out as needed.
86+
#git checkout master && git fetch --all && git rebase upstream/master
87+
#git log --format='%aN'
88+
#git rev-list --count --no-merges HEAD
89+
#git log | grep Author
90+
#git log --format='%aN' | sort -u | wc -l
91+
#echo "Total number of commits: $(git rev-list --count --no-merges HEAD)"
92+
done

project-metrics/release_metrics.py

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/python3
2+
3+
import os, sys
4+
import subprocess
5+
import requests
6+
7+
# Retrieve the git log of the current directory with line change stats (except renames and type changes)
8+
GIT_LOG_CMD = ['git', 'log', '--pretty=format:%aI%x20%aN%x20%ae', '--diff-filter=rt', '--numstat']
9+
#print(' '.join(GIT_LOG_CMD))
10+
11+
GITHUB_ACCESS_TOKEN = os.environ.get('GITHUB_ACCESS_TOKEN', '')
12+
GITHUB_ISSUES_URL = 'https://api.github.com/repos/edgexfoundry/%(repo)s/issues?state=all&access_token=%(access_token)s&since=%(since)s&page=%(page)s'
13+
14+
stats = 0
15+
commits = 0
16+
authors = dict()
17+
old_authors = set()
18+
repos = set()
19+
issues_opened = 0
20+
issues_closed = 0
21+
22+
basedir = os.getcwd()
23+
24+
from_date = sys.argv[1]
25+
to_date = sys.argv[2]
26+
#print('From: %s\tTo: %s' % (from_date, to_date))
27+
28+
# Iterate through every local repo passed as a command-line argument
29+
for repo in sys.argv[3:]:
30+
31+
repo_name = repo.split('/')[-1]
32+
issues_page = 1
33+
while (issues_page):
34+
repo_issues_url = GITHUB_ISSUES_URL % {'repo': repo_name, 'since': from_date, 'page': issues_page, 'access_token': GITHUB_ACCESS_TOKEN}
35+
#print('Calling: %s' % repo_issues_url)
36+
resp = requests.get(repo_issues_url)
37+
if resp.status_code == 200:
38+
issues = resp.json()
39+
for issue in issues:
40+
created = issue['created_at'][:10]
41+
if created >= from_date and created <= to_date:
42+
issues_opened += 1
43+
if issue['state'] == 'closed':
44+
closed = issue['closed_at'][:10]
45+
if closed >= from_date and closed <= to_date:
46+
issues_closed += 1
47+
if 'link' in resp.headers and 'rel="next"' in resp.headers['link']:
48+
issues_page+= 1
49+
else:
50+
break
51+
52+
os.chdir(os.path.join(basedir, repo))
53+
log = os.popen(' '.join(GIT_LOG_CMD))
54+
55+
date = None
56+
for line in log:
57+
args = line.split()
58+
if len(args) > 2 and len(args[0]) == 25:
59+
date = args[0][:10]
60+
if date >= from_date and date <= to_date:
61+
commits += 1
62+
repos.add(repo)
63+
64+
email = args[-1]
65+
author = ' '.join(args[1:-1])
66+
if email not in authors or len(author) > len(authors[email]):
67+
authors[email] = author
68+
elif date <= from_date:
69+
email = args[-1]
70+
old_authors.add(email)
71+
72+
elif len(args) == 3:
73+
if date is not None and date >= from_date and date <= to_date:
74+
if args[2].startswith('vendor'):
75+
continue
76+
try:
77+
added = abs(int(args[0]))
78+
removed = abs(int(args[1]))
79+
stats += max(added, removed)# the larger value
80+
except ValueError:
81+
pass
82+
83+
print('%d Active Repos' % len(repos))
84+
print('%d Issues Opened' % issues_opened)
85+
print('%d Issues Closed' % issues_closed)
86+
print('%d Commits' % commits)
87+
print('%d Lines of code' % stats)
88+
print('%d Authors' % len(authors))
89+
print('%d New Authors' % len(authors.keys() - old_authors))
90+
for email in sorted(authors, key=lambda e: authors.get(e).lower()):
91+
if email in old_authors:
92+
print('%s <%s>' % (authors[email], email))
93+
else:
94+
print('**%s** <%s>' % (authors[email], email))
95+
96+
97+

0 commit comments

Comments
 (0)