diff --git a/.gitignore b/.gitignore index 082302f..53aab97 100644 --- a/.gitignore +++ b/.gitignore @@ -2,19 +2,14 @@ *.env # Generated graph images: -*.png +assets/ # Library roots: -node_modules/ venv/ # Python caches: __pycache__/ -# Package manager configuration files: -package.json -package-lock.json - # IDE configuration files: .vscode .idea diff --git a/Makefile b/Makefile index a93a6e9..8f0d266 100644 --- a/Makefile +++ b/Makefile @@ -31,13 +31,14 @@ run-locally: venv run-container: @ # Run action in container docker build -t waka-readme-stats -f Dockerfile . - docker run --env-file $(ENV) waka-readme-stats + docker run --env-file $(ENV) -v ./assets/:/waka-readme-stats/assets/ waka-readme-stats .PHONY: run-container clean: @ # Clean all build files, including: libraries, package manager configs, docker images and containers rm -rf venv + rm -rf assets rm -f package*.json docker rm -f waka-readme-stats 2>/dev/null || true docker rmi $(docker images | grep "waka-readme-stats") 2> /dev/null || true diff --git a/sources/make_bar_graph.py b/sources/graph_drawer.py similarity index 64% rename from sources/make_bar_graph.py rename to sources/graph_drawer.py index 1081102..5c7dfc4 100644 --- a/sources/make_bar_graph.py +++ b/sources/graph_drawer.py @@ -1,69 +1,69 @@ -from typing import Dict -from os.path import join, dirname -from json import load - -import numpy as np -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt - -from download_manager import DownloadManager - - -MAX_LANGUAGES = 5 - - -async def build_graph(yearly_data: Dict) -> str: - """ - Draws graph of lines of code written by user by quarters of years. - Picks top `MAX_LANGUAGES` languages from each quarter only. - - :param yearly_data: GitHub user yearly data. - :return: String, path to graph file. - """ - colors = await DownloadManager.get_remote_yaml("linguist") - - languages_all_loc = dict() - years = len(yearly_data.keys()) - year_indexes = np.arange(years) - - for i, y in enumerate(sorted(yearly_data.keys())): - for q in yearly_data[y].keys(): - langs = sorted(yearly_data[y][q].keys(), key=lambda l: yearly_data[y][q][l], reverse=True)[0:MAX_LANGUAGES] - - for lang in langs: - if lang not in languages_all_loc: - languages_all_loc[lang] = np.array([[0] * years] * 4) - languages_all_loc[lang][q - 1][i] = yearly_data[y][q][lang] - - fig = plt.figure() - ax = fig.add_axes([0, 0, 1.5, 1]) - - language_handles = [] - cumulative = np.array([[0] * years] * 4) - - for key, value in languages_all_loc.items(): - color = colors[key]["color"] if colors[key]["color"] is not None else "w" - language_handles += [mpatches.Patch(color=color, label=key)] - - for quarter in range(4): - ax.bar(year_indexes + quarter * 0.21, value[quarter], 0.2, bottom=cumulative[quarter], color=color) - cumulative[quarter] = np.add(cumulative[quarter], value[quarter]) - - ax.set_ylabel("LOC added", fontdict=dict(weight="bold")) - ax.set_xticks(np.array([np.arange(i, i + 0.84, step=0.21) for i in year_indexes]).flatten(), labels=["Q1", "Q2", "Q3", "Q4"] * years) - - sax = ax.secondary_xaxis("top") - sax.set_xticks(year_indexes + 0.42, labels=sorted(yearly_data.keys())) - sax.spines["top"].set_visible(False) - - ax.legend(title="Language", handles=language_handles, loc="upper left", bbox_to_anchor=(1, 1), framealpha=0, title_fontproperties=dict(weight="bold")) - - sax.tick_params(axis="both", length=0) - sax.spines["top"].set_visible(False) - ax.spines["top"].set_visible(False) - ax.spines["right"].set_visible(False) - - plt.ylim(0, 1.05 * np.amax(cumulative)) - plt.savefig("bar_graph.png", bbox_inches="tight") - plt.close(fig) - return "bar_graph.png" +from typing import Dict + +import numpy as np +import matplotlib.patches as mpatches +import matplotlib.pyplot as plt + +from download_manager import DownloadManager + + +MAX_LANGUAGES = 5 + + +async def create_loc_graph(yearly_data: Dict, save_path: str): + """ + Draws graph of lines of code written by user by quarters of years. + Picks top `MAX_LANGUAGES` languages from each quarter only. + + :param yearly_data: GitHub user yearly data. + :param save_path: Path to save the graph file. + """ + colors = await DownloadManager.get_remote_yaml("linguist") + + years = len(yearly_data.keys()) + year_indexes = np.arange(years) + + all_languages = dict() + for year in yearly_data.values(): + for quarter in year.values(): + for language, loc in quarter.items(): + all_languages[language] = all_languages.get(language, 0) + loc + + top_languages_names = sorted(all_languages.keys(), key=lambda l: all_languages[l], reverse=True)[0:MAX_LANGUAGES] + top_languages = {language: np.array([[0] * years] * 4) for language in top_languages_names} + for index, year in enumerate(sorted(yearly_data.keys())): + for quarter, languages in yearly_data[year].items(): + for language, loc in {(lang, loc) for lang, loc in languages.items() if lang in top_languages}: + top_languages[language][quarter - 1][index] = yearly_data[year][quarter][language] + + fig = plt.figure() + ax = fig.add_axes([0, 0, 1.5, 1]) + + language_handles = [] + cumulative = np.array([[0] * years] * 4) + + for key, value in top_languages.items(): + color = colors[key]["color"] if colors[key]["color"] is not None else "w" + language_handles += [mpatches.Patch(color=color, label=key)] + + for quarter in range(4): + ax.bar(year_indexes + quarter * 0.21, value[quarter], 0.2, bottom=cumulative[quarter], color=color) + cumulative[quarter] = np.add(cumulative[quarter], value[quarter]) + + ax.set_ylabel("LOC added", fontdict=dict(weight="bold")) + ax.set_xticks(np.array([np.arange(i, i + 0.84, step=0.21) for i in year_indexes]).flatten(), labels=["Q1", "Q2", "Q3", "Q4"] * years) + + sax = ax.secondary_xaxis("top") + sax.set_xticks(year_indexes + 0.42, labels=sorted(yearly_data.keys())) + sax.spines["top"].set_visible(False) + + ax.legend(title="Language", handles=language_handles, loc="upper left", bbox_to_anchor=(1, 1), framealpha=0, title_fontproperties=dict(weight="bold")) + + sax.tick_params(axis="both", length=0) + sax.spines["top"].set_visible(False) + ax.spines["top"].set_visible(False) + ax.spines["right"].set_visible(False) + + plt.ylim(0, 1.05 * np.amax(cumulative)) + plt.savefig(save_path, bbox_inches="tight") + plt.close(fig) diff --git a/sources/loc.py b/sources/loc.py index 7b5cfb1..e1eca1e 100644 --- a/sources/loc.py +++ b/sources/loc.py @@ -5,10 +5,11 @@ from github import Github, InputGitAuthor, AuthenticatedUser import datetime from download_manager import DownloadManager -from make_bar_graph import build_graph +from graph_drawer import create_loc_graph class LinesOfCode: + GRAPH_PATH = "assets/bar_graph.png" def __init__(self, user: AuthenticatedUser, ghtoken, repositoryData, ignored_repos): self.g = Github(ghtoken) @@ -28,7 +29,7 @@ class LinesOfCode: return yearly_data async def plotLoc(self, yearly_data): - await build_graph(yearly_data) + await create_loc_graph(yearly_data, LinesOfCode.GRAPH_PATH) self.pushChart() def getQuarter(self, timeStamp): @@ -67,10 +68,10 @@ class LinesOfCode: def pushChart(self): repo = self.g.get_repo(f"{self.user.login}/{self.user.login}") committer = InputGitAuthor('readme-bot', '41898282+github-actions[bot]@users.noreply.github.com') - with open('bar_graph.png', 'rb') as input_file: + with open(LinesOfCode.GRAPH_PATH, 'rb') as input_file: data = input_file.read() try: - contents = repo.get_contents("charts/bar_graph.png") + contents = repo.get_contents(LinesOfCode.GRAPH_PATH) repo.update_file(contents.path, "Charts Updated", data, contents.sha, committer=committer) except Exception as e: - repo.create_file("charts/bar_graph.png", "Charts Added", data, committer=committer) + repo.create_file(LinesOfCode.GRAPH_PATH, "Charts Added", data, committer=committer) diff --git a/sources/main.py b/sources/main.py index a8f78f3..841d5e1 100644 --- a/sources/main.py +++ b/sources/main.py @@ -383,7 +383,7 @@ async def get_stats(github) -> str: if showLocChart.lower() in truthy: stats += '**' + translate['Timeline'] + '**\n\n' branch_name = github.get_repo(f'{user.login}/{user.login}').default_branch - stats = stats + '![Chart not found](https://raw.githubusercontent.com/' + user.login + '/' + user.login + '/' + branch_name + '/charts/bar_graph.png) \n\n' + stats = stats + '![Chart not found](https://raw.githubusercontent.com/' + user.login + '/' + user.login + '/' + branch_name + '/' + LinesOfCode.GRAPH_PATH + ') \n\n' if show_updated_date.lower() in truthy: now = datetime.datetime.utcnow()