diff options
Diffstat (limited to 'bot/seasons/halloween/hacktoberstats.py')
| -rw-r--r-- | bot/seasons/halloween/hacktoberstats.py | 65 |
1 files changed, 37 insertions, 28 deletions
diff --git a/bot/seasons/halloween/hacktoberstats.py b/bot/seasons/halloween/hacktoberstats.py index 81f11455..ce32ad9f 100644 --- a/bot/seasons/halloween/hacktoberstats.py +++ b/bot/seasons/halloween/hacktoberstats.py @@ -13,7 +13,9 @@ from discord.ext import commands log = logging.getLogger(__name__) -class HacktoberStats(commands.Cog): +class HacktoberStats: + """Hacktoberfest statistics Cog.""" + def __init__(self, bot): self.bot = bot self.link_json = Path("bot", "resources", "github_links.json") @@ -26,11 +28,13 @@ class HacktoberStats(commands.Cog): ) async def hacktoberstats_group(self, ctx: commands.Context, github_username: str = None): """ - If invoked without a subcommand or github_username, get the invoking user's stats if - they've linked their Discord name to GitHub using .stats link + Display an embed for a user's Hacktoberfest contributions. - If invoked with a github_username, get that user's contributions + If invoked without a subcommand or github_username, get the invoking user's stats if they've + linked their Discord name to GitHub using .stats link. If invoked with a github_username, + get that user's contributions """ + if not github_username: author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) @@ -51,7 +55,7 @@ class HacktoberStats(commands.Cog): @hacktoberstats_group.command(name="link") async def link_user(self, ctx: commands.Context, github_username: str = None): """ - Link the invoking user's Github github_username to their Discord ID + Link the invoking user's Github github_username to their Discord ID. Linked users are stored as a nested dict: { @@ -61,6 +65,7 @@ class HacktoberStats(commands.Cog): } } """ + author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) if github_username: if str(author_id) in self.linked_accounts.keys(): @@ -83,9 +88,8 @@ class HacktoberStats(commands.Cog): @hacktoberstats_group.command(name="unlink") async def unlink_user(self, ctx: commands.Context): - """ - Remove the invoking user's account link from the log - """ + """Remove the invoking user's account link from the log.""" + author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) stored_user = self.linked_accounts.pop(author_id, None) @@ -100,7 +104,7 @@ class HacktoberStats(commands.Cog): def load_linked_users(self) -> typing.Dict: """ - Load list of linked users from local JSON file + Load list of linked users from local JSON file. Linked users are stored as a nested dict: { @@ -110,6 +114,7 @@ class HacktoberStats(commands.Cog): } } """ + if self.link_json.exists(): logging.info(f"Loading linked GitHub accounts from '{self.link_json}'") with open(self.link_json, 'r') as fID: @@ -123,7 +128,7 @@ class HacktoberStats(commands.Cog): def save_linked_users(self): """ - Save list of linked users to local JSON file + Save list of linked users to local JSON file. Linked users are stored as a nested dict: { @@ -133,6 +138,7 @@ class HacktoberStats(commands.Cog): } } """ + logging.info(f"Saving linked_accounts to '{self.link_json}'") with open(self.link_json, 'w') as fID: json.dump(self.linked_accounts, fID, default=str) @@ -140,16 +146,15 @@ class HacktoberStats(commands.Cog): async def get_stats(self, ctx: commands.Context, github_username: str): """ - Query GitHub's API for PRs created by a GitHub user during the month of October that - do not have an 'invalid' tag + Query GitHub's API for PRs created by a GitHub user during the month of October. - For example: - !getstats heavysaturn + PRs with the 'invalid' tag are ignored If a valid github_username is provided, an embed is generated and posted to the channel Otherwise, post a helpful error message """ + async with ctx.typing(): prs = await self.get_october_prs(github_username) @@ -160,9 +165,8 @@ class HacktoberStats(commands.Cog): await ctx.send(f"No October GitHub contributions found for '{github_username}'") def build_embed(self, github_username: str, prs: typing.List[dict]) -> discord.Embed: - """ - Return a stats embed built from github_username's PRs - """ + """Return a stats embed built from github_username's PRs.""" + logging.info(f"Building Hacktoberfest embed for GitHub user: '{github_username}'") pr_stats = self._summarize_prs(prs) @@ -202,8 +206,9 @@ class HacktoberStats(commands.Cog): @staticmethod async def get_october_prs(github_username: str) -> typing.List[dict]: """ - Query GitHub's API for PRs created during the month of October by github_username - that do not have an 'invalid' tag + Query GitHub's API for PRs created during the month of October by github_username. + + PRs with an 'invalid' tag are ignored If PRs are found, return a list of dicts with basic PR information @@ -216,6 +221,7 @@ class HacktoberStats(commands.Cog): Otherwise, return None """ + logging.info(f"Generating Hacktoberfest PR query for GitHub user: '{github_username}'") base_url = "https://api.github.com/search/issues?q=" not_label = "invalid" @@ -265,20 +271,21 @@ class HacktoberStats(commands.Cog): @staticmethod def _get_shortname(in_url: str) -> str: """ - Extract shortname from https://api.github.com/repos/* URL + Extract shortname from https://api.github.com/repos/* URL. e.g. "https://api.github.com/repos/python-discord/seasonalbot" | V "python-discord/seasonalbot" """ + exp = r"https?:\/\/api.github.com\/repos\/([/\-\_\.\w]+)" return re.findall(exp, in_url)[0] @staticmethod def _summarize_prs(prs: typing.List[dict]) -> typing.Dict: """ - Generate statistics from an input list of PR dictionaries, as output by get_october_prs + Generate statistics from an input list of PR dictionaries, as output by get_october_prs. Return a dictionary containing: { @@ -286,13 +293,14 @@ class HacktoberStats(commands.Cog): "top5": [(repo_shortname, ncontributions), ...] } """ + contributed_repos = [pr["repo_shortname"] for pr in prs] return {"n_prs": len(prs), "top5": Counter(contributed_repos).most_common(5)} @staticmethod def _build_top5str(stats: typing.List[tuple]) -> str: """ - Build a string from the Top 5 contributions that is compatible with a discord.Embed field + Build a string from the Top 5 contributions that is compatible with a discord.Embed field. Top 5 contributions should be a list of tuples, as output in the stats dictionary by _summarize_prs @@ -301,6 +309,7 @@ class HacktoberStats(commands.Cog): n contribution(s) to [shortname](url) ... """ + baseURL = "https://www.github.com/" contributionstrs = [] for repo in stats['top5']: @@ -311,9 +320,8 @@ class HacktoberStats(commands.Cog): @staticmethod def _contributionator(n: int) -> str: - """ - Return "contribution" or "contributions" based on the value of n - """ + """Return "contribution" or "contributions" based on the value of n.""" + if n == 1: return "contribution" else: @@ -321,9 +329,8 @@ class HacktoberStats(commands.Cog): @staticmethod def _author_mention_from_context(ctx: commands.Context) -> typing.Tuple: - """ - Return stringified Message author ID and mentionable string from commands.Context - """ + """Return stringified Message author ID and mentionable string from commands.Context.""" + author_id = str(ctx.message.author.id) author_mention = ctx.message.author.mention @@ -331,5 +338,7 @@ class HacktoberStats(commands.Cog): def setup(bot): + """Hacktoberstats Cog load.""" + bot.add_cog(HacktoberStats(bot)) log.info("HacktoberStats cog loaded") |