Merge pull request #116 from joe733/workshop

fix: adds lang count option properly
This commit is contained in:
Jovial Joe Jayarson 2023-02-09 00:29:20 +05:30 committed by GitHub
commit 413150be53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 621 additions and 510 deletions

View File

@ -4,9 +4,7 @@
</center> </center>
# Dev Metrics in Readme [![UnitTests](https://github.com/athul/waka-readme/actions/workflows/testing.yml/badge.svg)](https://github.com/athul/waka-readme/actions/workflows/testing.yml) # Dev Metrics in Readme [![Unit Tests](https://github.com/athul/waka-readme/actions/workflows/testing.yml/badge.svg?branch=master)](https://github.com/athul/waka-readme/actions/workflows/testing.yml) ![Python Version](https://img.shields.io/badge/python-v3.11-blue)
<center>
[WakaTime](https://wakatime.com) weekly metrics on your profile readme. [WakaTime](https://wakatime.com) weekly metrics on your profile readme.
@ -14,8 +12,6 @@
:speech_balloon: **Forum** | [GitHub discussions][gh_discuss] :speech_balloon: **Forum** | [GitHub discussions][gh_discuss]
</center>
## New to WakaTime? ## New to WakaTime?
> Nope? Skip to [prep work](#prep-work). > Nope? Skip to [prep work](#prep-work).
@ -32,15 +28,17 @@ Alternatively, you can also fetch data from WakaTime compatible services like [W
## Prep Work ## Prep Work
A GitHub repository and a README file is required. We'll be making use of readme in the [profile repository][profile_readme]\*. A GitHub repository and a `README.md` file is required. We'll be making use of readme in the [profile repository][profile_readme]\*.
- Save the README file after copy-pasting the following special comments. Your dev-metics will show up in between. `waka` here can be replaced by any string as long as you set the `SECTION_NAME` environment variable [as per the Tweaks section](tweaks). - Save the `README.md` file after copy-pasting the following special comments. Your dev-metics will show up in between.
```md ```md
<!--START_SECTION:waka--> <!--START_SECTION:waka-->
<!--END_SECTION:waka--> <!--END_SECTION:waka-->
``` ```
"`waka`" can be replaced by any alphanumeric string with the `SECTION_NAME` environment variable. See the [#tweaks](#tweaks) section for more.
- Navigate to your repo's `Settings > Secrets` and add a new secret _named_ `WAKATIME_API_KEY` with your API key as it's _value_. - Navigate to your repo's `Settings > Secrets` and add a new secret _named_ `WAKATIME_API_KEY` with your API key as it's _value_.
> Or use the url <https://github.com/USERNAME/USERNAME/settings/secrets/actions/new> by replacing the `USERNAME` with your own username. > Or use the url <https://github.com/USERNAME/USERNAME/settings/secrets/actions/new> by replacing the `USERNAME` with your own username.
@ -90,6 +88,7 @@ There are many flags that you can tweak to suit your taste!
| `SHOW_TIME` | `true` | `false`, `true` | Displays the amount of time spent for each language | | `SHOW_TIME` | `true` | `false`, `true` | Displays the amount of time spent for each language |
| `SHOW_TOTAL` | `false` | `false`, `true` | Show total coding time | | `SHOW_TOTAL` | `false` | `false`, `true` | Show total coding time |
| `SHOW_MASKED_TIME` | `false` | `false`, `true` | Adds total coding time including unclassified languages (overrides: `SHOW_TOTAL`) | | `SHOW_MASKED_TIME` | `false` | `false`, `true` | Adds total coding time including unclassified languages (overrides: `SHOW_TOTAL`) |
| `LANG_COUNT` | `5` | Any reasonable number | Number of languages to be displayed |
# Example # Example
@ -117,6 +116,7 @@ jobs:
TIME_RANGE: all_time TIME_RANGE: all_time
SHOW_TIME: true SHOW_TIME: true
SHOW_MASKED_TIME: true SHOW_MASKED_TIME: true
LANG_COUNT: 10
``` ```
**`README.md`** **`README.md`**

View File

@ -49,6 +49,11 @@ inputs:
default: "last_7_days" default: "last_7_days"
required: false required: false
LANG_COUNT:
description: "Maximum number of languages to be shown"
default: "5"
required: false
SHOW_TIME: SHOW_TIME:
description: "Displays the amount of time spent for each language" description: "Displays the amount of time spent for each language"
default: "true" default: "true"

51
main.py
View File

@ -154,7 +154,7 @@ class WakaInput:
show_time: str | bool = os.getenv('INPUT_SHOW_TIME') or False show_time: str | bool = os.getenv('INPUT_SHOW_TIME') or False
show_total_time: str | bool = os.getenv('INPUT_SHOW_TOTAL') or False show_total_time: str | bool = os.getenv('INPUT_SHOW_TOTAL') or False
show_masked_time: str | bool = os.getenv('INPUT_SHOW_MASKED_TIME') or False show_masked_time: str | bool = os.getenv('INPUT_SHOW_MASKED_TIME') or False
language_count: str | bool = os.getenv('INPUT_LANGUAGE_COUNT') or "5" language_count: str | int = os.getenv('INPUT_LANG_COUNT') or 5
def validate_input(self) -> bool: def validate_input(self) -> bool:
""" """
@ -178,7 +178,7 @@ class WakaInput:
self.show_time = strtobool(self.show_time) self.show_time = strtobool(self.show_time)
self.show_total_time = strtobool(self.show_total_time) self.show_total_time = strtobool(self.show_total_time)
self.show_masked_time = strtobool(self.show_masked_time) self.show_masked_time = strtobool(self.show_masked_time)
except ValueError as err: except (ValueError, AttributeError) as err:
logger.error(err) logger.error(err)
return False return False
@ -202,6 +202,11 @@ class WakaInput:
logger.debug('Using default time range: last_7_days') logger.debug('Using default time range: last_7_days')
self.time_range = 'last_7_days' self.time_range = 'last_7_days'
if not str(self.language_count).isnumeric():
logger.warning('Invalid language count')
logger.debug('Using default language count: 5')
self.language_count = 5
logger.debug('Input validation complete\n') logger.debug('Input validation complete\n')
return True return True
@ -249,11 +254,11 @@ def make_graph(block_style: str, percent: float, gr_len: int, lg_nm: str = '', /
graph_bar += block_style[remainder_block] if remainder_block > 0 else '' graph_bar += block_style[remainder_block] if remainder_block > 0 else ''
graph_bar += block_style[0] * (gr_len - len(graph_bar)) graph_bar += block_style[0] * (gr_len - len(graph_bar))
logger.debug(f'{lg_nm or "..."} graph generated') logger.debug(f'"{lg_nm or "..."}" graph generated')
return graph_bar return graph_bar
def prep_content(stats: dict[Any, Any], language_count: str = 5, /) -> str: def prep_content(stats: dict[str, Any], language_count: int = 5, /) -> str:
""" """
WakaReadme Prepare Markdown WakaReadme Prepare Markdown
--------------------------- ---------------------------
@ -261,14 +266,9 @@ def prep_content(stats: dict[Any, Any], language_count: str = 5, /) -> str:
Prepared markdown content from the fetched statistics Prepared markdown content from the fetched statistics
``` ```
""" """
logger.debug('Making contents')
contents = '' contents = ''
# Check if any data exists
if not (lang_info := stats.get('languages')):
logger.debug('The data seems to be empty, please wait for a day')
contents += 'No activity tracked'
return contents
# make title # make title
if wk_i.show_title: if wk_i.show_title:
contents += make_title(stats.get('start'), stats.get('end')) + '\n\n' contents += make_title(stats.get('start'), stats.get('end')) + '\n\n'
@ -284,19 +284,26 @@ def prep_content(stats: dict[Any, Any], language_count: str = 5, /) -> str:
): ):
contents += f'Total Time: {total_time}\n\n' contents += f'Total Time: {total_time}\n\n'
# make content lang_info: list[dict[str, int | float | str]] | None = []
logger.debug('Making contents')
# Check if any language data exists
if not (lang_info := stats.get('languages')):
logger.debug('The API data seems to be empty, please wait for a day')
contents += 'No activity tracked'
return contents
# make lang content
pad_len = len( pad_len = len(
# comment if it feels way computationally expensive # comment if it feels way computationally expensive
max((l.get('name') for l in lang_info), key=len) max((str(lng['name']) for lng in lang_info), key=len)
# and then don't for get to set pad_len to say 13 :) # and then don't for get to set pad_len to say 13 :)
) )
for idx, lang in enumerate(lang_info): for idx, lang in enumerate(lang_info):
lang_name = lang.get('name') lang_name = str(lang['name'])
# >>> add languages to filter here <<< # >>> add languages to filter here <<<
# if lang_name in {...}: continue # if lang_name in {...}: continue
lang_time = lang.get('text') if wk_i.show_time else '' lang_time = str(lang['text']) if wk_i.show_time else ''
lang_ratio = lang.get('percent') lang_ratio = float(lang['percent'])
lang_bar = make_graph( lang_bar = make_graph(
wk_i.block_style, lang_ratio, wk_i.graph_length, lang_name wk_i.block_style, lang_ratio, wk_i.graph_length, lang_name
) )
@ -305,14 +312,14 @@ def prep_content(stats: dict[Any, Any], language_count: str = 5, /) -> str:
f'{lang_time: <16}{lang_bar} ' + f'{lang_time: <16}{lang_bar} ' +
f'{lang_ratio:.2f}'.zfill(5) + ' %\n' f'{lang_ratio:.2f}'.zfill(5) + ' %\n'
) )
if idx >= int(language_count) or lang_name == 'Other': if idx >= language_count or lang_name == 'Other':
break break
logger.debug('Contents were made\n') logger.debug('Contents were made\n')
return contents.rstrip('\n') return contents.rstrip('\n')
def fetch_stats() -> dict[Any, Any] | None: def fetch_stats() -> dict[str, Any] | None:
""" """
WakaReadme Fetch Stats WakaReadme Fetch Stats
---------------------- ----------------------
@ -320,7 +327,7 @@ def fetch_stats() -> dict[Any, Any] | None:
Returns statistics as JSON string Returns statistics as JSON string
""" """
attempts = 4 attempts = 4
statistic: dict[str, dict[Any, Any]] = {} statistic: dict[str, dict[str, Any]] = {}
encoded_key = str( encoded_key = str(
b64encode(bytes(str(wk_i.waka_key), 'utf-8')), 'utf-8' b64encode(bytes(str(wk_i.waka_key), 'utf-8')), 'utf-8'
) )
@ -341,7 +348,7 @@ def fetch_stats() -> dict[Any, Any] | None:
timeout=30 * (5 - attempts) timeout=30 * (5 - attempts)
)).status_code != 200: )).status_code != 200:
resp_message += f'{conn_info}' if ( resp_message += f'{conn_info}' if (
conn_info := resp.json().get("message") conn_info := resp.json().get('message')
) else '' ) else ''
logger.debug( logger.debug(
f'API response #{5 - attempts}: {resp.status_code}{resp.reason}{resp_message}' f'API response #{5 - attempts}: {resp.status_code}{resp.reason}{resp_message}'
@ -374,8 +381,8 @@ def churn(old_readme: str, /) -> str | None:
sys.exit(1) sys.exit(1)
# processing content # processing content
try: try:
generated_content = prep_content(waka_stats, wk_i.language_count) generated_content = prep_content(waka_stats, int(wk_i.language_count))
except AttributeError as err: except (AttributeError, KeyError, ValueError) as err:
logger.error(f'Unable to read API data | {err}\n') logger.error(f'Unable to read API data | {err}\n')
sys.exit(1) sys.exit(1)
print(generated_content, '\n', sep='') print(generated_content, '\n', sep='')

1011
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
[tool.poetry] [tool.poetry]
name = "waka-readme" name = "waka-readme"
version = "0.1.7" version = "0.1.7"
description = "Wakatime Weekly Metrics on your Profile Readme." description = "Wakatime Weekly Metrics on your Profile Readme"
authors = ["Athul Cyriac Ajay <athul8720@gmail.com>"] authors = ["Athul Cyriac Ajay <athul8720@gmail.com>"]
license = "MIT" license = "MIT"
readme = "README.md" readme = "README.md"
@ -9,14 +9,14 @@ readme = "README.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.11" python = "^3.11"
requests = "^2.28.1" faker = "^16.6.1"
PyGithub = "^1.57" requests = "^2.28.2"
Faker = "^15.3.3" pygithub = "^1.57"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
autopep8 = "^2.0.0" autopep8 = "^2.0.1"
pylint = "^2.15.7" pylint = "^2.16.1"
python-dotenv = "^0.21.0" python-dotenv = "^0.21.1"
loguru = "^0.6.0" loguru = "^0.6.0"
bandit = "^1.7.4" bandit = "^1.7.4"

View File

@ -1,12 +1,15 @@
''' """
Tests for the main.py Tests for the main.py
''' """
# standard
from importlib import import_module from importlib import import_module
from dataclasses import dataclass from dataclasses import dataclass # , field
from itertools import product from itertools import product
# from pathlib import Path
# from inspect import cleandoc # from inspect import cleandoc
# from json import loads # from typing import Any
# from json import load
import unittest import unittest
import sys import sys
import os import os
@ -24,7 +27,9 @@ except ImportError as err:
class TestData: class TestData:
"""Test Data""" """Test Data"""
# for future tests # for future tests
# waka_json: dict | None = None # waka_json: dict[str, dict[str, Any]] = field(
# default_factory=lambda: {}
# )
bar_percent: tuple[int | float, ...] | None = None bar_percent: tuple[int | float, ...] | None = None
graph_blocks: tuple[str, ...] | None = None graph_blocks: tuple[str, ...] | None = None
waka_graphs: tuple[list[str], ...] | None = None waka_graphs: tuple[list[str], ...] | None = None
@ -33,8 +38,12 @@ class TestData:
def populate(self) -> None: def populate(self) -> None:
"""Populate Test Data""" """Populate Test Data"""
# for future tests # for future tests
# with open(file='tests/sample_data.json', mode='rt', encoding='utf-8') as wkf: # with open(
# self.waka_json = loads(wkf.read()) # file=Path(__file__).parent / 'sample_data.json',
# encoding='utf-8',
# mode='rt',
# ) as wkf:
# self.waka_json = load(wkf)
self.bar_percent = ( self.bar_percent = (
0, 100, 49.999, 50, 25, 75, 3.14, 9.901, 87.334, 87.333, 4.666, 4.667 0, 100, 49.999, 50, 25, 75, 3.14, 9.901, 87.334, 87.333, 4.666, 4.667
@ -116,11 +125,14 @@ class TestMain(unittest.TestCase):
r'From: \d{2} \w{3,9} \d{4} - To: \d{2} \w{3,9} \d{4}' r'From: \d{2} \w{3,9} \d{4} - To: \d{2} \w{3,9} \d{4}'
) )
# Known test limits def test_strtobool(self) -> None:
# # prep_content() and churn(): """Test string to bool"""
# requires additional modifications such as changing self.assertTrue(prime.strtobool('Yes'))
# globally passed values to parametrically passing them self.assertFalse(prime.strtobool('nO'))
# # fetch_stats(): would required HTTP Authentication self.assertTrue(prime.strtobool(True))
self.assertRaises(AttributeError, prime.strtobool, None)
self.assertRaises(ValueError, prime.strtobool, 'yo!')
self.assertRaises(AttributeError, prime.strtobool, 20.5)
tds = TestData() tds = TestData()