documentation added, black linter added

This commit is contained in:
pseusys
2023-02-17 22:41:08 +01:00
parent b36e374320
commit 2e494aa87b
12 changed files with 230 additions and 105 deletions

View File

@@ -6,7 +6,7 @@ on:
jobs:
publish-server-image:
name: Publish 'waka-readme-stats' image
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️

View File

@@ -6,7 +6,7 @@ on:
jobs:
lint:
name: Run codestyle check
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- name: Checkout 🛎️
@@ -21,4 +21,4 @@ jobs:
run: pip install -r requirements.txt
- name: Run Codestyle ✔️
run: flake8 --max-line-length=160 --exclude venv,assets .
run: flake8 --max-line-length=160 --exclude venv,assets . && black --line-length=160 --check --exclude='/venv/|/assets/' .

View File

@@ -38,9 +38,10 @@ run-container:
.PHONY: run-container
lint:
lint: venv
@ # Run flake8 linter
flake8 --max-line-length=160 --exclude venv,assets .
black --line-length=160 --check --exclude='/venv/|/assets/' .
.PHONY: lint
clean:

View File

@@ -13,5 +13,6 @@ numpy~=1.24
httpx~=0.23
PyYAML~=6.0
# Codestyle checking module:
# Codestyle checking modules:
flake8~=6.0
black~=23.1

View File

@@ -7,8 +7,8 @@ import matplotlib.pyplot as plt
from manager_download import DownloadManager as DM
MAX_LANGUAGES = 5
GRAPH_PATH = "assets/bar_graph.png"
MAX_LANGUAGES = 5 # Number of top languages to add to chart, for each year quarter
GRAPH_PATH = "assets/bar_graph.png" # Chart saving path.
async def create_loc_graph(yearly_data: Dict, save_path: str):
@@ -27,7 +27,7 @@ async def create_loc_graph(yearly_data: Dict, save_path: str):
languages_all_loc = dict()
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]
langs = sorted(yearly_data[y][q].keys(), key=lambda n: yearly_data[y][q][n], reverse=True)[0:MAX_LANGUAGES]
for lang in langs:
if lang not in languages_all_loc:

View File

@@ -10,24 +10,39 @@ from manager_github import GitHubManager as GHM
from manager_localization import LocalizationManager as LM
DAY_TIME_EMOJI = ["🌞", "🌆", "🌃", "🌙"]
DAY_TIME_NAMES = ["Morning", "Daytime", "Evening", "Night"]
WEEK_DAY_NAMES = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
DAY_TIME_EMOJI = ["🌞", "🌆", "🌃", "🌙"] # Emojis, representing different times of day.
DAY_TIME_NAMES = ["Morning", "Daytime", "Evening", "Night"] # Localization identifiers for different times of day.
WEEK_DAY_NAMES = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] # Localization identifiers for different days of week.
class Symbol(Enum):
"""
Symbol version enum.
Allows to retrieve symbols pairs by calling `Symbol.get_symbols(version)`.
"""
VERSION_1 = "", ""
VERSION_2 = "", ""
VERSION_3 = "", ""
@staticmethod
def get_symbols(version: int) -> Tuple[str, str]:
"""
Retrieves symbols pair for specified version.
:param version: Required symbols version.
:returns: Two strings for filled and empty symbol value in a tuple.
"""
return Symbol[f"VERSION_{version}"].value
def make_graph(percent: float):
"""
Make progress graph from API graph
Make text progress bar.
Length of the progress bar is 25 characters.
:param percent: Completion percent of the progress bar.
:return: The string progress bar representation.
"""
done_block, empty_block = Symbol.get_symbols(EM.SYMBOL_VERSION)
percent_quart = round(percent / 4)
@@ -36,7 +51,20 @@ def make_graph(percent: float):
def make_list(data: List = None, names: List[str] = None, texts: List[str] = None, percents: List[float] = None, top_num: int = 5, sort: bool = True) -> str:
"""
Make List
Make list of text progress bars with supportive info.
Each row has the following structure: [name of the measure] [quantity description (with words)] [progress bar] [total percentage].
Name of the measure: up to 25 characters.
Quantity description: how many _things_ were found, up to 20 characters.
Progress bar: measure percentage, 25 characters.
Total percentage: floating point percentage.
:param data: list of dictionaries, each of them containing a measure (name, text and percent).
:param names: list of names (names of measure), overloads data if defined.
:param texts: list of texts (quantity descriptions), overloads data if defined.
:param percents: list of percents (total percentages), overloads data if defined.
:param top_num: how many measures to display, default: 5.
:param sort: if measures should be sorted by total percentage, default: True.
:returns: The string representation of the list.
"""
if data is not None:
names = [value for item in data for key, value in item.items() if key == "name"] if names is None else names
@@ -46,10 +74,16 @@ def make_list(data: List = None, names: List[str] = None, texts: List[str] = Non
data = list(zip(names, texts, percents))
top_data = sorted(data[:top_num], key=lambda record: record[2]) if sort else data[:top_num]
data_list = [f"{n[:25]}{' ' * (25 - len(n))}{t}{' ' * (20 - len(t))}{make_graph(p)} {p:05.2f} % " for n, t, p in top_data]
return '\n'.join(data_list)
return "\n".join(data_list)
async def make_commit_day_time_list(time_zone: str) -> str:
"""
Calculate commit-related info, how many commits were made, and at what time of day and day of week.
:param time_zone: User time zone.
:returns: string representation of statistics.
"""
stats = str()
result = await DM.get_remote_graphql("repos_contributed_to", username=GHM.USER.login)
@@ -62,8 +96,8 @@ async def make_commit_day_time_list(time_zone: str) -> str:
result = await DM.get_remote_graphql("repo_committed_dates", owner=repository["owner"]["login"], name=repository["name"], id=GHM.USER.node_id)
committed_dates = result["data"]["repository"]["defaultBranchRef"]["target"]["history"]["edges"]
for committedDate in committed_dates:
local_date = datetime.strptime(committedDate["node"]["committedDate"], "%Y-%m-%dT%H:%M:%SZ")
for committed_date in committed_dates:
local_date = datetime.strptime(committed_date["node"]["committedDate"], "%Y-%m-%dT%H:%M:%SZ")
date = local_date.replace(tzinfo=utc).astimezone(timezone(time_zone))
day_times[date.hour // 6] += 1
@@ -74,14 +108,14 @@ async def make_commit_day_time_list(time_zone: str) -> str:
day_times = day_times[1:] + day_times[:1]
dt_names = [f"{DAY_TIME_EMOJI[i]} {LM.t(DAY_TIME_NAMES[i])}" for i in range(len(day_times))]
dt_texts = [f'{day_time} commits' for day_time in day_times]
dt_texts = [f"{day_time} commits" for day_time in day_times]
dt_percents = [round((day_time / sum_day) * 100, 2) for day_time in day_times]
title = LM.t("I am an Early") if sum(day_times[0:2]) >= sum(day_times[2:4]) else LM.t("I am a Night")
stats += f"**{title}** \n\n```text\n{make_list(names=dt_names, texts=dt_texts, percents=dt_percents, top_num=7, sort=False)}\n```\n"
if EM.SHOW_DAYS_OF_WEEK:
wd_names = [LM.t(week_day) for week_day in WEEK_DAY_NAMES]
wd_texts = [f'{week_day} commits' for week_day in week_days]
wd_texts = [f"{week_day} commits" for week_day in week_days]
wd_percents = [round((week_day / sum_week) * 100, 2) for week_day in week_days]
title = LM.t("I am Most Productive on") % wd_names[wd_percents.index(max(wd_percents))]
stats += f"📅 **{title}** \n\n```text\n{make_list(names=wd_names, texts=wd_texts, percents=wd_percents, top_num=7, sort=False)}\n```\n"
@@ -90,6 +124,12 @@ async def make_commit_day_time_list(time_zone: str) -> str:
def make_language_per_repo_list(repositories: Dict) -> str:
"""
Calculate language-related info, how many repositories in what language user has.
:param repositories: User repositories.
:returns: string representation of statistics.
"""
language_count = dict()
repos_with_language = [repo for repo in repositories["data"]["user"]["repositories"]["edges"] if repo["node"]["primaryLanguage"] is not None]
for repo in repos_with_language:

View File

@@ -7,7 +7,7 @@ from urllib.parse import quote
from humanize import intword, naturalsize, intcomma, precisedelta
from manager_download import init_download_manager, DownloadManager as DM, close_download_manager
from manager_download import init_download_manager, DownloadManager as DM
from manager_environment import EnvironmentManager as EM
from manager_github import init_github_manager, GitHubManager as GHM
from manager_localization import init_localization_manager, LocalizationManager as LM
@@ -17,6 +17,12 @@ from graphics_list_formatter import make_list, make_commit_day_time_list, make_l
async def get_waka_time_stats() -> str:
"""
Collects user info from wakatime.
Info includes most common commit time, timezone, language, editors, projects and OSs.
:returns: String representation of the info.
"""
stats = str()
data = await DM.get_remote_json("waka_latest")
@@ -52,7 +58,13 @@ async def get_waka_time_stats() -> str:
return stats
async def get_short_github_info():
async def get_short_github_info() -> str:
"""
Collects user info from GitHub public profile.
The stats include: disk usage, contributions number, whether the user has opted to hire, public and private repositories number.
:returns: String representation of the info.
"""
stats = f"**🐱 {LM.t('My GitHub Data')}** \n\n"
if GHM.USER.disk_usage is None:
@@ -64,7 +76,7 @@ async def get_short_github_info():
data = await DM.get_remote_json("github_stats")
if len(data["years"]) > 0:
contributions = LM.t('Contributions in the year') % (intcomma(data["years"][0]['total']), data["years"][0]['year'])
contributions = LM.t("Contributions in the year") % (intcomma(data["years"][0]["total"]), data["years"][0]["year"])
stats += f"> 🏆 {contributions}\n > \n"
opted_to_hire = GHM.USER.hireable
@@ -90,7 +102,10 @@ async def get_short_github_info():
async def get_stats() -> str:
"""
Gets API data and returns markdown progress
Creates new README.md content from all the acquired statistics from all places.
The readme includes data from wakatime, contributed lines of code number, GitHub profile info and last updated date.
:returns: String representation of README.md contents.
"""
stats = str()
repositories = await DM.get_remote_graphql("user_repository_list", username=GHM.USER.login, id=GHM.USER.node_id)
@@ -125,8 +140,7 @@ async def get_stats() -> str:
await create_loc_graph(yearly_data, GRAPH_PATH)
GHM.update_chart(GRAPH_PATH)
chart_path = f"{GHM.USER.login}/{GHM.USER.login}/{GHM.branch()}/{GRAPH_PATH}"
stats += '**' + LM.t('Timeline') + '**\n\n'
stats += f"![Lines of Code chart](https://raw.githubusercontent.com/{chart_path})\n\n"
stats += f"**{LM.t('Timeline')}**\n\n![Lines of Code chart](https://raw.githubusercontent.com/{chart_path})\n\n"
if EM.SHOW_UPDATED_DATE:
stats += f"\n Last Updated on {datetime.now().strftime(EM.UPDATED_DATE_FORMAT)} UTC"
@@ -135,16 +149,20 @@ async def get_stats() -> str:
async def main():
"""
Application main function.
Initializes all managers, collects user info and updates README.md if necessary.
"""
init_github_manager()
await init_download_manager()
init_localization_manager()
if GHM.update_readme(await get_stats()):
print("Readme updated!")
await close_download_manager()
await DM.close_remote_resources()
if __name__ == '__main__':
if __name__ == "__main__":
start_time = datetime.now()
run(main())
run_delta = datetime.now() - start_time

View File

@@ -1,3 +1,4 @@
from asyncio import Task
from hashlib import md5
from json import dumps
from string import Template
@@ -11,6 +12,7 @@ from manager_github import GitHubManager as GHM
GITHUB_API_QUERIES = {
# Query to collect info about all user repositories, including: is it a fork, name and owner login.
"repos_contributed_to": """
{
user(login: "$username") {
@@ -25,6 +27,7 @@ GITHUB_API_QUERIES = {
}
}
}""",
# Query to collect info about all commits in user repositories, including: commit date.
"repo_committed_dates": """
{
repository(owner: "$owner", name: "$name") {
@@ -43,6 +46,7 @@ GITHUB_API_QUERIES = {
}
}
}""",
# Query to collect info about all repositories user created or collaborated on, including: name, primary language and owner login.
"user_repository_list": """
{
user(login: "$username") {
@@ -62,6 +66,7 @@ GITHUB_API_QUERIES = {
}
}
""",
# Query to collect info about user commits to given repository, including: commit date, additions and deletions numbers.
"repo_commit_list": """
{
repository(owner: "$owner", name: "$name") {
@@ -90,7 +95,7 @@ GITHUB_API_QUERIES = {
}
}
}
"""
""",
}
@@ -100,23 +105,15 @@ async def init_download_manager():
- Setup headers for GitHub GraphQL requests.
- Launch static queries in background.
"""
await DownloadManager.load_remote_resources({
await DownloadManager.load_remote_resources(
{
"linguist": "https://cdn.jsdelivr.net/gh/github/linguist@master/lib/linguist/languages.yml",
"waka_latest": f"https://wakatime.com/api/v1/users/current/stats/last_7_days?api_key={EM.WAKATIME_API_KEY}",
"waka_all": f"https://wakatime.com/api/v1/users/current/all_time_since_today?api_key={EM.WAKATIME_API_KEY}",
"github_stats": f"https://github-contributions.vercel.app/api/v1/{GHM.USER.login}"
}, {
"Authorization": f"Bearer {EM.GH_TOKEN}"
})
async def close_download_manager():
"""
Initialize download manager:
- Setup headers for GitHub GraphQL requests.
- Launch static queries in background.
"""
await DownloadManager.close_remote_resources("linguist", "waka_latest", "waka_all", "github_stats")
"github_stats": f"https://github-contributions.vercel.app/api/v1/{GHM.USER.login}",
},
{"Authorization": f"Bearer {EM.GH_TOKEN}"},
)
class DownloadManager:
@@ -130,6 +127,7 @@ class DownloadManager:
DownloadManager launches all static queries asynchronously upon initialization and caches their results.
It also executes dynamic queries upon request and caches result.
"""
_client = AsyncClient(timeout=60.0)
_REMOTE_RESOURCES_CACHE = dict()
@@ -145,14 +143,16 @@ class DownloadManager:
DownloadManager._client.headers = github_headers
@staticmethod
async def close_remote_resources(*resource: str):
async def close_remote_resources():
"""
Prepare DownloadManager to launch GitHub API queries and launch all static queries.
:param resources: Dictionary of static queries, "IDENTIFIER": "URL".
:param github_headers: Dictionary of headers for GitHub API queries.
Close DownloadManager and cancel all un-awaited static web queries.
Await all queries that could not be cancelled.
"""
for resource in [DownloadManager._REMOTE_RESOURCES_CACHE[r] for r in resource if isinstance(DownloadManager._REMOTE_RESOURCES_CACHE[r], Awaitable)]:
for resource in DownloadManager._REMOTE_RESOURCES_CACHE.values():
if isinstance(resource, Task):
resource.cancel()
elif isinstance(resource, Awaitable):
await resource
@staticmethod
async def _get_remote_resource(resource: str, convertor: Optional[Callable[[bytes], Dict]]) -> Dict:
@@ -208,9 +208,7 @@ class DownloadManager:
"""
key = f"{query}_{md5(dumps(kwargs, sort_keys=True).encode('utf-8')).digest()}"
if key not in DownloadManager._REMOTE_RESOURCES_CACHE:
res = await DownloadManager._client.post("https://api.github.com/graphql", json={
"query": Template(GITHUB_API_QUERIES[query]).substitute(kwargs)
})
res = await DownloadManager._client.post("https://api.github.com/graphql", json={"query": Template(GITHUB_API_QUERIES[query]).substitute(kwargs)})
DownloadManager._REMOTE_RESOURCES_CACHE[key] = res
else:
res = DownloadManager._REMOTE_RESOURCES_CACHE[key]

View File

@@ -2,35 +2,44 @@ from os import getenv, environ
class EnvironmentManager:
_TRUTHY = ['true', '1', 't', 'y', 'yes']
"""
Class for handling all environmental variables used by the action.
There are only two required variables: `INPUT_GH_TOKEN` and `INPUT_WAKATIME_API_KEY`.
The others have a provided default value.
For all boolean variables a 'truthy'-list is checked (not only true/false, but also 1, t, y and yes are accepted).
List variable `IGNORED_REPOS` is split and parsed.
Integer variable `SYMBOL_VERSION` is parsed.
"""
GH_TOKEN = environ['INPUT_GH_TOKEN']
WAKATIME_API_KEY = environ['INPUT_WAKATIME_API_KEY']
_TRUTHY = ["true", "1", "t", "y", "yes"]
GH_TOKEN = environ["INPUT_GH_TOKEN"]
WAKATIME_API_KEY = environ["INPUT_WAKATIME_API_KEY"]
SECTION_NAME = getenv("INPUT_SECTION_NAME", "waka")
BRANCH_NAME = getenv('INPUT_PUSH_BRANCH_NAME', "")
BRANCH_NAME = getenv("INPUT_PUSH_BRANCH_NAME", "")
SHOW_OS = getenv('INPUT_SHOW_OS', "False").lower() in _TRUTHY
SHOW_PROJECTS = getenv('INPUT_SHOW_PROJECTS', "True").lower() in _TRUTHY
SHOW_EDITORS = getenv('INPUT_SHOW_EDITORS', "True").lower() in _TRUTHY
SHOW_TIMEZONE = getenv('INPUT_SHOW_TIMEZONE', "True").lower() in _TRUTHY
SHOW_COMMIT = getenv('INPUT_SHOW_COMMIT', "True").lower() in _TRUTHY
SHOW_LANGUAGE = getenv('INPUT_SHOW_LANGUAGE', "True").lower() in _TRUTHY
SHOW_LINES_OF_CODE = getenv('INPUT_SHOW_LINES_OF_CODE', "False").lower() in _TRUTHY
SHOW_LANGUAGE_PER_REPO = getenv('INPUT_SHOW_LANGUAGE_PER_REPO', "True").lower() in _TRUTHY
SHOW_LOC_CHART = getenv('INPUT_SHOW_LOC_CHART', "True").lower() in _TRUTHY
SHOW_DAYS_OF_WEEK = getenv('INPUT_SHOW_DAYS_OF_WEEK', "True").lower() in _TRUTHY
SHOW_PROFILE_VIEWS = getenv('INPUT_SHOW_PROFILE_VIEWS', "True").lower() in _TRUTHY
SHOW_SHORT_INFO = getenv('INPUT_SHOW_SHORT_INFO', "True").lower() in _TRUTHY
SHOW_UPDATED_DATE = getenv('INPUT_SHOW_UPDATED_DATE', "True").lower() in _TRUTHY
SHOW_TOTAL_CODE_TIME = getenv('INPUT_SHOW_TOTAL_CODE_TIME', "True").lower() in _TRUTHY
SHOW_OS = getenv("INPUT_SHOW_OS", "False").lower() in _TRUTHY
SHOW_PROJECTS = getenv("INPUT_SHOW_PROJECTS", "True").lower() in _TRUTHY
SHOW_EDITORS = getenv("INPUT_SHOW_EDITORS", "True").lower() in _TRUTHY
SHOW_TIMEZONE = getenv("INPUT_SHOW_TIMEZONE", "True").lower() in _TRUTHY
SHOW_COMMIT = getenv("INPUT_SHOW_COMMIT", "True").lower() in _TRUTHY
SHOW_LANGUAGE = getenv("INPUT_SHOW_LANGUAGE", "True").lower() in _TRUTHY
SHOW_LINES_OF_CODE = getenv("INPUT_SHOW_LINES_OF_CODE", "False").lower() in _TRUTHY
SHOW_LANGUAGE_PER_REPO = getenv("INPUT_SHOW_LANGUAGE_PER_REPO", "True").lower() in _TRUTHY
SHOW_LOC_CHART = getenv("INPUT_SHOW_LOC_CHART", "True").lower() in _TRUTHY
SHOW_DAYS_OF_WEEK = getenv("INPUT_SHOW_DAYS_OF_WEEK", "True").lower() in _TRUTHY
SHOW_PROFILE_VIEWS = getenv("INPUT_SHOW_PROFILE_VIEWS", "True").lower() in _TRUTHY
SHOW_SHORT_INFO = getenv("INPUT_SHOW_SHORT_INFO", "True").lower() in _TRUTHY
SHOW_UPDATED_DATE = getenv("INPUT_SHOW_UPDATED_DATE", "True").lower() in _TRUTHY
SHOW_TOTAL_CODE_TIME = getenv("INPUT_SHOW_TOTAL_CODE_TIME", "True").lower() in _TRUTHY
COMMIT_BY_ME = getenv('INPUT_COMMIT_BY_ME', "False").lower() in _TRUTHY
COMMIT_MESSAGE = getenv('INPUT_COMMIT_MESSAGE', "Updated with Dev Metrics")
COMMIT_USERNAME = getenv('INPUT_COMMIT_USERNAME', "")
COMMIT_EMAIL = getenv('INPUT_COMMIT_EMAIL', "")
COMMIT_BY_ME = getenv("INPUT_COMMIT_BY_ME", "False").lower() in _TRUTHY
COMMIT_MESSAGE = getenv("INPUT_COMMIT_MESSAGE", "Updated with Dev Metrics")
COMMIT_USERNAME = getenv("INPUT_COMMIT_USERNAME", "")
COMMIT_EMAIL = getenv("INPUT_COMMIT_EMAIL", "")
LOCALE = getenv('INPUT_LOCALE', "en")
UPDATED_DATE_FORMAT = getenv('INPUT_UPDATED_DATE_FORMAT', "%d/%m/%Y %H:%M:%S")
IGNORED_REPOS = getenv('INPUT_IGNORED_REPOS', "").replace(' ', '').split(',')
SYMBOL_VERSION = int(getenv('INPUT_SYMBOL_VERSION'))
LOCALE = getenv("INPUT_LOCALE", "en")
UPDATED_DATE_FORMAT = getenv("INPUT_UPDATED_DATE_FORMAT", "%d/%m/%Y %H:%M:%S")
IGNORED_REPOS = getenv("INPUT_IGNORED_REPOS", "").replace(" ", "").split(",")
SYMBOL_VERSION = int(getenv("INPUT_SYMBOL_VERSION"))

View File

@@ -8,6 +8,8 @@ from manager_environment import EnvironmentManager as EM
def init_github_manager():
"""
Initialize GitHub manager.
Current user, user readme repo and readme file are downloaded.
"""
GitHubManager.prepare_github_env()
print(f"Current user: {GitHubManager.USER.login}")
@@ -16,61 +18,79 @@ def init_github_manager():
class GitHubManager:
USER: AuthenticatedUser
REPO: Repository
README: ContentFile
README_CONTENTS: str
_README: ContentFile
_README_CONTENTS: str
_START_COMMENT = f'<!--START_SECTION:{EM.SECTION_NAME}-->'
_END_COMMENT = f'<!--END_SECTION:{EM.SECTION_NAME}-->'
_START_COMMENT = f"<!--START_SECTION:{EM.SECTION_NAME}-->"
_END_COMMENT = f"<!--END_SECTION:{EM.SECTION_NAME}-->"
_README_REGEX = f"{_START_COMMENT}[\\s\\S]+{_END_COMMENT}"
@staticmethod
def prepare_github_env():
"""
Download and store for future use:
- Current GitHub user.
- Named repo of the user [username]/[username].
- README.md file of this repo.
- Parsed contents of the file.
"""
github = Github(EM.GH_TOKEN)
GitHubManager.USER = github.get_user()
GitHubManager.REPO = github.get_repo(f"{GitHubManager.USER.login}/{GitHubManager.USER.login}")
GitHubManager.README = GitHubManager.REPO.get_readme()
GitHubManager.README_CONTENTS = str(b64decode(GitHubManager.README.content), 'utf-8')
GitHubManager._REPO = github.get_repo(f"{GitHubManager.USER.login}/{GitHubManager.USER.login}")
GitHubManager._README = GitHubManager._REPO.get_readme()
GitHubManager._README_CONTENTS = str(b64decode(GitHubManager._README.content), "utf-8")
@staticmethod
def _generate_new_readme(stats: str):
def _generate_new_readme(stats: str) -> str:
"""
Generate a new Readme.md
Generates new README.md file, inserts its contents between start and end tags.
:param stats: contents to insert.
:returns: new README.md string.
"""
readme_stats = f"{GitHubManager._START_COMMENT}\n{stats}\n{GitHubManager._END_COMMENT}"
return sub(GitHubManager._README_REGEX, readme_stats, GitHubManager.README_CONTENTS)
return sub(GitHubManager._README_REGEX, readme_stats, GitHubManager._README_CONTENTS)
@staticmethod
def _get_author():
def _get_author() -> InputGitAuthor:
"""
Gets GitHub commit author specified by environmental variables.
It is the user himself or a 'readme-bot'.
:returns: Commit author.
"""
if EM.COMMIT_BY_ME:
return InputGitAuthor(
GitHubManager.USER.login or EM.COMMIT_USERNAME,
GitHubManager.USER.email or EM.COMMIT_EMAIL
)
return InputGitAuthor(GitHubManager.USER.login or EM.COMMIT_USERNAME, GitHubManager.USER.email or EM.COMMIT_EMAIL)
else:
return InputGitAuthor(
EM.COMMIT_USERNAME or 'readme-bot',
EM.COMMIT_EMAIL or '41898282+github-actions[bot]@users.noreply.github.com'
)
return InputGitAuthor(EM.COMMIT_USERNAME or "readme-bot", EM.COMMIT_EMAIL or "41898282+github-actions[bot]@users.noreply.github.com")
@staticmethod
def branch() -> str:
"""
Gets name of branch to commit to specified by environmental variables.
It is the default branch (regularly, 'main' or 'master') or a branch specified by user.
:returns: Commit author.
"""
return GitHubManager.REPO.default_branch if EM.BRANCH_NAME == "" else EM.BRANCH_NAME
@staticmethod
def update_readme(stats: str) -> bool:
"""
Updates readme with given data if necessary.
Uses commit author, commit message and branch name specified by environmental variables.
:returns: whether the README.md file was updated or not.
"""
new_readme = GitHubManager._generate_new_readme(stats)
if new_readme != GitHubManager.README_CONTENTS:
if new_readme != GitHubManager._README_CONTENTS:
GitHubManager.REPO.update_file(
path=GitHubManager.README.path,
path=GitHubManager._README.path,
message=EM.COMMIT_MESSAGE,
content=new_readme,
sha=GitHubManager.README.sha,
sha=GitHubManager._README.sha,
branch=GitHubManager.branch(),
committer=GitHubManager._get_author()
committer=GitHubManager._get_author(),
)
return True
else:
@@ -78,6 +98,12 @@ class GitHubManager:
@staticmethod
def update_chart(chart_path: str):
"""
Updates lines of code chart.
Uses commit author, commit message and branch name specified by environmental variables.
:param chart_path: path to saved lines of code chart.
"""
with open(chart_path, "rb") as input_file:
data = input_file.read()
try:

View File

@@ -7,19 +7,37 @@ from manager_environment import EnvironmentManager as EM
def init_localization_manager():
"""
Initialize localization manager.
Load GUI translations JSON file.
"""
LocalizationManager.load_localization("translation.json")
class LocalizationManager:
"""
Class for handling localization (and maybe other file IO in future).
Stores localization in dictionary.
"""
_LOCALIZATION: Dict[str, str] = dict()
@staticmethod
def load_localization(file: str):
with open(join(dirname(__file__), file), encoding='utf-8') as config_file:
"""
Read localization file and store locale defined with environmental variable.
:param file: Localization file path, related to current file (in sources root).
"""
with open(join(dirname(__file__), file), encoding="utf-8") as config_file:
data = load(config_file)
LocalizationManager._LOCALIZATION = data[EM.LOCALE]
@staticmethod
def t(key: str) -> str:
"""
Translate string to current localization.
:param key: Localization key.
:returns: Translation string.
"""
return LocalizationManager._LOCALIZATION[key]

View File

@@ -8,6 +8,13 @@ from manager_github import GitHubManager as GHM
async def calculate_yearly_commit_data(repositories: Dict) -> Dict:
"""
Calculate commit data by years.
Commit data includes difference between contribution additions and deletions in each quarter of each recorded year.
:param repositories: user repositories info dictionary.
:returns: Commit quarter yearly data dictionary.
"""
yearly_data = dict()
total = len(repositories["data"]["user"]["repositories"]["edges"])
for ind, repo in enumerate(repositories["data"]["user"]["repositories"]["edges"]):
@@ -17,7 +24,14 @@ async def calculate_yearly_commit_data(repositories: Dict) -> Dict:
return yearly_data
async def update_yearly_data_with_commit_stats(repo_details: Dict, yearly_data: Dict) -> Dict:
async def update_yearly_data_with_commit_stats(repo_details: Dict, yearly_data: Dict):
"""
Updates yearly commit data with commits from given repository.
Skips update if the commit isn't related to any repository.
:param repo_details: Dictionary with information about the given repository.
:param yearly_data: Yearly data dictionary to update.
"""
commit_data = await DM.get_remote_graphql("repo_commit_list", owner=repo_details["owner"]["login"], name=repo_details["name"], id=GHM.USER.node_id)
if commit_data["data"]["repository"] is None:
@@ -36,4 +50,4 @@ async def update_yearly_data_with_commit_stats(repo_details: Dict, yearly_data:
yearly_data[curr_year][quarter] = dict()
if repo_details["primaryLanguage"]["name"] not in yearly_data[curr_year][quarter]:
yearly_data[curr_year][quarter][repo_details["primaryLanguage"]["name"]] = 0
yearly_data[curr_year][quarter][repo_details["primaryLanguage"]["name"]] += (commit["additions"] - commit["deletions"])
yearly_data[curr_year][quarter][repo_details["primaryLanguage"]["name"]] += commit["additions"] - commit["deletions"]