Skip to content

Commit

Permalink
Merge pull request #124 from sommersoft/blinka-stats
Browse files Browse the repository at this point in the history
Add Blinka To Daily Report
  • Loading branch information
kattni authored Dec 19, 2019
2 parents 428b109 + f6af199 commit ebf9b9f
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 29 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ env.sh
*.swp
.libraries/*
.cp_org/*
.blinka/*
60 changes: 33 additions & 27 deletions adabot/circuitpython_libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import copy
import datetime
import re
Expand All @@ -36,6 +37,8 @@
from adabot.lib import circuitpython_library_validators as cirpy_lib_vals
from adabot.lib import common_funcs
from adabot.lib import assign_hacktober_label as hacktober
from adabot.lib import blinka_funcs
from adabot import circuitpython_library_download_stats as dl_stats

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

repos = common_funcs.list_repos()
repos = common_funcs.list_repos(include_repos=('Adafruit_Blinka',))
output_handler("Found {} repos to check.".format(len(repos)))
bundle_submodules = common_funcs.get_bundle_submodules()
output_handler("Found {} submodules in the bundle.".format(len(bundle_submodules)))
github_user = common_funcs.whois_github_user()
output_handler("Running GitHub checks as " + github_user)
need_work = 0
lib_insights = {
"merged_prs": 0,
"closed_prs": 0,
"new_prs": 0,
"active_prs": 0,
"open_prs": [],
"pr_authors": set(),
"pr_merged_authors": set(),
"pr_reviewers": set(),
"closed_issues": 0,
"new_issues": 0,
"active_issues": 0,
"open_issues": [],
"issue_authors": set(),
"issue_closers": set(),
"hacktober_assigned": 0,
"hacktober_removed": 0,
}
core_insights = copy.deepcopy(lib_insights)
for k in core_insights:
if isinstance(core_insights[k], set):
core_insights[k] = set()
if isinstance(core_insights[k], list):
core_insights[k] = []

lib_insights = common_funcs.InsightData()
blinka_insights = common_funcs.InsightData()
core_insights = common_funcs.InsightData()
core_insights["milestones"] = dict()

repo_needs_work = []
since = datetime.datetime.now() - datetime.timedelta(days=7)
repos_by_error = {}
Expand Down Expand Up @@ -176,8 +160,10 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
"{0} ({1} days)".format(repo["html_url"], error[1])
)
insights = lib_insights
if (repo["name"] == "circuitpython" and
repo["owner"]["login"] == "adafruit"):
if repo["owner"]["login"] == "adafruit":
if repo["name"] == "Adafruit_Blinka":
insights = blinka_insights
elif repo["name"] == "circuitpython":
insights = core_insights
errors = validator.gather_insights(repo, insights, since)
if errors:
Expand Down Expand Up @@ -267,6 +253,24 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
if error_count <= error_depth or error in list_repos_for_errors:
output_handler("\n".join([" * " + x for x in repos_by_error[error]]))

output_handler()
output_handler("Blinka")
print_pr_overview(blinka_insights)
output_handler("* {} open pull requests".format(len(blinka_insights["open_prs"])))
sorted_prs = sorted(blinka_insights["open_prs"],
key=lambda days: int(pr_sort_re.search(days).group(1)),
reverse=True)
for pr in sorted_prs:
output_handler(" * {}".format(pr))
print_issue_overview(blinka_insights)
output_handler("* {} open issues".format(len(blinka_insights["open_issues"])))
output_handler(" * https://github.com/adafruit/Adafruit_Blinka/issues")
blinka_dl, _ = dl_stats.pypistats_get('adafruit-blinka')
output_handler("* PyPI Downloads in the last week: {}".format(blinka_dl))
output_handler(
"Number of supported boards: {}".format(blinka_funcs.board_count())
)

def output_handler(message="", quiet=False):
"""Handles message output to prompt/file for print_*() functions."""
if output_filename is not None:
Expand Down Expand Up @@ -339,6 +343,8 @@ def print_circuitpython_download_stats():
total[release["tag_name"]] = 0
total[release["tag_name"]] += count

output_handler("Number of supported boards: {}".format(len(by_board)))
output_handler()
output_handler("Download stats by board:")
output_handler()
by_board_list = [["Board", "{}".format(stable_tag.strip(" ")), "{}".format(prerelease_tag.strip(" "))],]
Expand Down
57 changes: 57 additions & 0 deletions adabot/lib/blinka_funcs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# The MIT License (MIT)
#
# Copyright (c) 2019 Michael Schroeder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import os
import pathlib
import re

import sh
from sh.contrib import git


def board_count():
""" Retrieve the number of boards currently supported by Adafruit_Blinka,
via Adafruit_Python_PlatformDetect.
"""
platdetec_boards_re = re.compile(r'^[A-Z]\w+\s+\=\s[\'\"]\w+[\'\"]',
re.MULTILINE)
board_count = 0
working_dir = os.getcwd()
blinka_dir = pathlib.Path(working_dir, '.blinka')
repo_url = 'https://github.com/adafruit/Adafruit_Python_PlatformDetect.git'

if not blinka_dir.exists():
try:
git.clone(repo_url, blinka_dir.resolve(), '--depth', '1')
except sh.ErrorReturnCode_128:
print("Failed to clone Adafruit_Blinka. Board count not determined.")
board_count = "Error"

src_board_path = blinka_dir / 'adafruit_platformdetect/board.py'
if src_board_path.exists():
board_content = ""
with open(src_board_path, 'r') as board_py:
board_content = board_py.read()
content_re = platdetec_boards_re.findall(board_content)
board_count = len(content_re)

return board_count
59 changes: 57 additions & 2 deletions adabot/lib/common_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# GitHub API Serch has stopped returning the core repo for some reason. Tried several
# different search params, and came up emtpy. Hardcoding it as a failsafe.

import collections
import datetime
import os
import re
Expand Down Expand Up @@ -157,10 +158,13 @@ def is_repo_in_bundle(repo_clone_url, bundle_submodules):
# Failed to find the repo as a submodule of the libraries folders.
return False

def list_repos():
def list_repos(*, include_repos=None):
"""Return a list of all Adafruit repositories that start with
Adafruit_CircuitPython. Each list item is a dictionary of GitHub API
repository state.
:param: tuple,list include_repos: A tuple or list of repositories to ensure
are included.
"""
repos = []
result = github.get("/search/repositories",
Expand Down Expand Up @@ -189,11 +193,23 @@ def list_repos():
break
# Subsequent links have our access token already so we use requests directly.
result = requests.get(link, timeout=30)
if "circuitpython" not in [repo["name"] for repo in repos]:

repo_names = [repo["name"] for repo in repos]

if "circuitpython" not in repo_names:
core = github.get(core_repo_url)
if core.ok:
repos.append(core.json())

if include_repos:
for repo in include_repos:
if repo not in repo_names:
add_repo = github.get("/repos/adafruit/" + repo)
if add_repo.ok:
repos.append(add_repo.json())
else:
print("list_repos(): Failed to retrieve '{}'".format(repo))

return repos

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

return user

class InsightData(collections.UserDict):
""" Container class for holding insight data (issues, PRs, etc).
"""

def __init__(self):
self.data = {
"merged_prs": 0,
"closed_prs": 0,
"new_prs": 0,
"active_prs": 0,
"open_prs": [],
"pr_authors": set(),
"pr_merged_authors": set(),
"pr_reviewers": set(),
"closed_issues": 0,
"new_issues": 0,
"active_issues": 0,
"open_issues": [],
"issue_authors": set(),
"issue_closers": set(),
"hacktober_assigned": 0,
"hacktober_removed": 0,
}

def __contains__(self, key):
return key in self.data

def __getitem__(self, key):
return self.data[key]

def __setitem__(self, key, value):
self.data[key] = value

def keys(self):
return self.data.keys()

def copy(self):
return self.data.copy()

0 comments on commit ebf9b9f

Please sign in to comment.