Skip to content

Commit ebf9b9f

Browse files
authored
Merge pull request #124 from sommersoft/blinka-stats
Add Blinka To Daily Report
2 parents 428b109 + f6af199 commit ebf9b9f

File tree

4 files changed

+148
-29
lines changed

4 files changed

+148
-29
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ env.sh
77
*.swp
88
.libraries/*
99
.cp_org/*
10+
.blinka/*

adabot/circuitpython_libraries.py

+33-27
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2222
# THE SOFTWARE.
23+
2324
import copy
2425
import datetime
2526
import re
@@ -36,6 +37,8 @@
3637
from adabot.lib import circuitpython_library_validators as cirpy_lib_vals
3738
from adabot.lib import common_funcs
3839
from adabot.lib import assign_hacktober_label as hacktober
40+
from adabot.lib import blinka_funcs
41+
from adabot import circuitpython_library_download_stats as dl_stats
3942

4043
# Setup ArgumentParser
4144
cmd_line_parser = argparse.ArgumentParser(
@@ -109,38 +112,19 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
109112
latest_pylint = pylint_info.json()["info"]["version"]
110113
output_handler("Latest pylint is: {}".format(latest_pylint))
111114

112-
repos = common_funcs.list_repos()
115+
repos = common_funcs.list_repos(include_repos=('Adafruit_Blinka',))
113116
output_handler("Found {} repos to check.".format(len(repos)))
114117
bundle_submodules = common_funcs.get_bundle_submodules()
115118
output_handler("Found {} submodules in the bundle.".format(len(bundle_submodules)))
116119
github_user = common_funcs.whois_github_user()
117120
output_handler("Running GitHub checks as " + github_user)
118121
need_work = 0
119-
lib_insights = {
120-
"merged_prs": 0,
121-
"closed_prs": 0,
122-
"new_prs": 0,
123-
"active_prs": 0,
124-
"open_prs": [],
125-
"pr_authors": set(),
126-
"pr_merged_authors": set(),
127-
"pr_reviewers": set(),
128-
"closed_issues": 0,
129-
"new_issues": 0,
130-
"active_issues": 0,
131-
"open_issues": [],
132-
"issue_authors": set(),
133-
"issue_closers": set(),
134-
"hacktober_assigned": 0,
135-
"hacktober_removed": 0,
136-
}
137-
core_insights = copy.deepcopy(lib_insights)
138-
for k in core_insights:
139-
if isinstance(core_insights[k], set):
140-
core_insights[k] = set()
141-
if isinstance(core_insights[k], list):
142-
core_insights[k] = []
122+
123+
lib_insights = common_funcs.InsightData()
124+
blinka_insights = common_funcs.InsightData()
125+
core_insights = common_funcs.InsightData()
143126
core_insights["milestones"] = dict()
127+
144128
repo_needs_work = []
145129
since = datetime.datetime.now() - datetime.timedelta(days=7)
146130
repos_by_error = {}
@@ -176,8 +160,10 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
176160
"{0} ({1} days)".format(repo["html_url"], error[1])
177161
)
178162
insights = lib_insights
179-
if (repo["name"] == "circuitpython" and
180-
repo["owner"]["login"] == "adafruit"):
163+
if repo["owner"]["login"] == "adafruit":
164+
if repo["name"] == "Adafruit_Blinka":
165+
insights = blinka_insights
166+
elif repo["name"] == "circuitpython":
181167
insights = core_insights
182168
errors = validator.gather_insights(repo, insights, since)
183169
if errors:
@@ -267,6 +253,24 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
267253
if error_count <= error_depth or error in list_repos_for_errors:
268254
output_handler("\n".join([" * " + x for x in repos_by_error[error]]))
269255

256+
output_handler()
257+
output_handler("Blinka")
258+
print_pr_overview(blinka_insights)
259+
output_handler("* {} open pull requests".format(len(blinka_insights["open_prs"])))
260+
sorted_prs = sorted(blinka_insights["open_prs"],
261+
key=lambda days: int(pr_sort_re.search(days).group(1)),
262+
reverse=True)
263+
for pr in sorted_prs:
264+
output_handler(" * {}".format(pr))
265+
print_issue_overview(blinka_insights)
266+
output_handler("* {} open issues".format(len(blinka_insights["open_issues"])))
267+
output_handler(" * https://github.com/adafruit/Adafruit_Blinka/issues")
268+
blinka_dl, _ = dl_stats.pypistats_get('adafruit-blinka')
269+
output_handler("* PyPI Downloads in the last week: {}".format(blinka_dl))
270+
output_handler(
271+
"Number of supported boards: {}".format(blinka_funcs.board_count())
272+
)
273+
270274
def output_handler(message="", quiet=False):
271275
"""Handles message output to prompt/file for print_*() functions."""
272276
if output_filename is not None:
@@ -339,6 +343,8 @@ def print_circuitpython_download_stats():
339343
total[release["tag_name"]] = 0
340344
total[release["tag_name"]] += count
341345

346+
output_handler("Number of supported boards: {}".format(len(by_board)))
347+
output_handler()
342348
output_handler("Download stats by board:")
343349
output_handler()
344350
by_board_list = [["Board", "{}".format(stable_tag.strip(" ")), "{}".format(prerelease_tag.strip(" "))],]

adabot/lib/blinka_funcs.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Michael Schroeder
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
import os
24+
import pathlib
25+
import re
26+
27+
import sh
28+
from sh.contrib import git
29+
30+
31+
def board_count():
32+
""" Retrieve the number of boards currently supported by Adafruit_Blinka,
33+
via Adafruit_Python_PlatformDetect.
34+
"""
35+
platdetec_boards_re = re.compile(r'^[A-Z]\w+\s+\=\s[\'\"]\w+[\'\"]',
36+
re.MULTILINE)
37+
board_count = 0
38+
working_dir = os.getcwd()
39+
blinka_dir = pathlib.Path(working_dir, '.blinka')
40+
repo_url = 'https://github.com/adafruit/Adafruit_Python_PlatformDetect.git'
41+
42+
if not blinka_dir.exists():
43+
try:
44+
git.clone(repo_url, blinka_dir.resolve(), '--depth', '1')
45+
except sh.ErrorReturnCode_128:
46+
print("Failed to clone Adafruit_Blinka. Board count not determined.")
47+
board_count = "Error"
48+
49+
src_board_path = blinka_dir / 'adafruit_platformdetect/board.py'
50+
if src_board_path.exists():
51+
board_content = ""
52+
with open(src_board_path, 'r') as board_py:
53+
board_content = board_py.read()
54+
content_re = platdetec_boards_re.findall(board_content)
55+
board_count = len(content_re)
56+
57+
return board_count

adabot/lib/common_funcs.py

+57-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
# GitHub API Serch has stopped returning the core repo for some reason. Tried several
2424
# different search params, and came up emtpy. Hardcoding it as a failsafe.
2525

26+
import collections
2627
import datetime
2728
import os
2829
import re
@@ -157,10 +158,13 @@ def is_repo_in_bundle(repo_clone_url, bundle_submodules):
157158
# Failed to find the repo as a submodule of the libraries folders.
158159
return False
159160

160-
def list_repos():
161+
def list_repos(*, include_repos=None):
161162
"""Return a list of all Adafruit repositories that start with
162163
Adafruit_CircuitPython. Each list item is a dictionary of GitHub API
163164
repository state.
165+
166+
:param: tuple,list include_repos: A tuple or list of repositories to ensure
167+
are included.
164168
"""
165169
repos = []
166170
result = github.get("/search/repositories",
@@ -189,11 +193,23 @@ def list_repos():
189193
break
190194
# Subsequent links have our access token already so we use requests directly.
191195
result = requests.get(link, timeout=30)
192-
if "circuitpython" not in [repo["name"] for repo in repos]:
196+
197+
repo_names = [repo["name"] for repo in repos]
198+
199+
if "circuitpython" not in repo_names:
193200
core = github.get(core_repo_url)
194201
if core.ok:
195202
repos.append(core.json())
196203

204+
if include_repos:
205+
for repo in include_repos:
206+
if repo not in repo_names:
207+
add_repo = github.get("/repos/adafruit/" + repo)
208+
if add_repo.ok:
209+
repos.append(add_repo.json())
210+
else:
211+
print("list_repos(): Failed to retrieve '{}'".format(repo))
212+
197213
return repos
198214

199215
def repo_is_on_pypi(repo):
@@ -262,3 +278,42 @@ def whois_github_user():
262278
user = github.get("/user").json()["login"]
263279

264280
return user
281+
282+
class InsightData(collections.UserDict):
283+
""" Container class for holding insight data (issues, PRs, etc).
284+
"""
285+
286+
def __init__(self):
287+
self.data = {
288+
"merged_prs": 0,
289+
"closed_prs": 0,
290+
"new_prs": 0,
291+
"active_prs": 0,
292+
"open_prs": [],
293+
"pr_authors": set(),
294+
"pr_merged_authors": set(),
295+
"pr_reviewers": set(),
296+
"closed_issues": 0,
297+
"new_issues": 0,
298+
"active_issues": 0,
299+
"open_issues": [],
300+
"issue_authors": set(),
301+
"issue_closers": set(),
302+
"hacktober_assigned": 0,
303+
"hacktober_removed": 0,
304+
}
305+
306+
def __contains__(self, key):
307+
return key in self.data
308+
309+
def __getitem__(self, key):
310+
return self.data[key]
311+
312+
def __setitem__(self, key, value):
313+
self.data[key] = value
314+
315+
def keys(self):
316+
return self.data.keys()
317+
318+
def copy(self):
319+
return self.data.copy()

0 commit comments

Comments
 (0)