maint: misc. refactorings, fixes, updates
- puts tests in the correct location of github workflow - fixes `.env` values not loading via `load_dotenv` - required early loading - corrects many static type linting errors - combines all inputs into a single class with validation - formats markdown & python files, as well as output - slightly improved log messages, caught potential attribute error - updates dependencies
This commit is contained in:
parent
72af24c8af
commit
bd7707fc5a
@ -1,4 +1,4 @@
|
|||||||
name: WakaReadme
|
name: UnitTests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -11,16 +11,15 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python 3.11
|
- name: Set up Python 3.11
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
curl -sSL https://install.python-poetry.org | python -
|
curl -sSL https://install.python-poetry.org | python -
|
||||||
export PATH="$HOME/.poetry/bin:${PATH}"
|
export PATH="$HOME/.poetry/bin:${PATH}"
|
||||||
source $HOME/.poetry/env
|
|
||||||
poetry install
|
poetry install
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: |
|
run: |
|
@ -37,9 +37,9 @@
|
|||||||
|
|
||||||
where `WakaReadmeDev` is the docker container name. Then execute `bash` in the container:
|
where `WakaReadmeDev` is the docker container name. Then execute `bash` in the container:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ docker exec -it 'WakaReadmeDev' bash
|
$ docker exec -it 'WakaReadmeDev' bash
|
||||||
```
|
```
|
||||||
|
|
||||||
3. For development, you can attach code editor of your choice to this container.
|
3. For development, you can attach code editor of your choice to this container.
|
||||||
4. Export environnement variables with edits, as required:
|
4. Export environnement variables with edits, as required:
|
||||||
@ -58,7 +58,7 @@
|
|||||||
INPUT_REPOSITORY='<REPOSITORY SLUG>'
|
INPUT_REPOSITORY='<REPOSITORY SLUG>'
|
||||||
INPUT_COMMIT_MESSAGE='<COMMIT MESSAGE>'
|
INPUT_COMMIT_MESSAGE='<COMMIT MESSAGE>'
|
||||||
INPUT_SHOW_TITLE='True'
|
INPUT_SHOW_TITLE='True'
|
||||||
INPUT_SECTION_NAME='wakacustom'
|
INPUT_SECTION_NAME='waka'
|
||||||
INPUT_BLOCKS='->'
|
INPUT_BLOCKS='->'
|
||||||
INPUT_SHOW_TIME='True'
|
INPUT_SHOW_TIME='True'
|
||||||
INPUT_SHOW_TOTAL='True'
|
INPUT_SHOW_TOTAL='True'
|
||||||
@ -72,6 +72,7 @@
|
|||||||
# poetry shell
|
# poetry shell
|
||||||
# set -a && . ./.env && set +a # optional
|
# set -a && . ./.env && set +a # optional
|
||||||
(venv)# python -m main --dev
|
(venv)# python -m main --dev
|
||||||
|
(venv)# python -m unittest discover # run tests
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Later, to remove stop and remove the container:
|
5. Later, to remove stop and remove the container:
|
||||||
@ -93,7 +94,7 @@
|
|||||||
> ```json
|
> ```json
|
||||||
> {
|
> {
|
||||||
> "terminal.integrated.commandsToSkipShell": [
|
> "terminal.integrated.commandsToSkipShell": [
|
||||||
> "-workbench.action.quickOpenView"
|
> "-workbench.action.quickOpenView"
|
||||||
> ]
|
> ]
|
||||||
> }
|
> }
|
||||||
> ```
|
> ```
|
||||||
@ -124,6 +125,7 @@
|
|||||||
INPUT_REPOSITORY='<REPOSITORY SLUG>'
|
INPUT_REPOSITORY='<REPOSITORY SLUG>'
|
||||||
INPUT_COMMIT_MESSAGE='<COMMIT MESSAGE>'
|
INPUT_COMMIT_MESSAGE='<COMMIT MESSAGE>'
|
||||||
INPUT_SHOW_TITLE='True'
|
INPUT_SHOW_TITLE='True'
|
||||||
|
INPUT_SECTION_NAME='waka'
|
||||||
INPUT_BLOCKS='->'
|
INPUT_BLOCKS='->'
|
||||||
INPUT_SHOW_TIME='True'
|
INPUT_SHOW_TIME='True'
|
||||||
INPUT_SHOW_TOTAL='True'
|
INPUT_SHOW_TOTAL='True'
|
||||||
@ -136,4 +138,5 @@
|
|||||||
```console
|
```console
|
||||||
$ set -a && . ./.env && set +a # optional
|
$ set -a && . ./.env && set +a # optional
|
||||||
(venv)$ python -m main --dev
|
(venv)$ python -m main --dev
|
||||||
|
(venv)$ python -m unittest discover # run tests
|
||||||
```
|
```
|
||||||
|
77
README.md
77
README.md
@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
# Dev Metrics in Readme [](https://travis-ci.com/athul/waka-readme)
|
# Dev Metrics in Readme [](https://github.com/athul/waka-readme/actions/workflows/testing.yml)
|
||||||
|
|
||||||
|
<center>
|
||||||
|
|
||||||
[WakaTime](https://wakatime.com) weekly metrics on your profile readme.
|
[WakaTime](https://wakatime.com) weekly metrics on your profile readme.
|
||||||
|
|
||||||
@ -12,6 +14,8 @@
|
|||||||
|
|
||||||
: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,43 +36,43 @@ A GitHub repository and a README file is required. We'll be making use of 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 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).
|
||||||
|
|
||||||
```md
|
```md
|
||||||
|
<!--START_SECTION:waka-->
|
||||||
|
<!--END_SECTION:waka-->
|
||||||
|
```
|
||||||
|
|
||||||
<!--START_SECTION:waka-->
|
- Navigate to your repo's `Settings > Secrets` and add a new secret _named_ `WAKATIME_API_KEY` with your API key as it's _value_.
|
||||||
<!--END_SECTION:waka-->
|
|
||||||
|
|
||||||
```
|
> Or use the url <https://github.com/USERNAME/USERNAME/settings/secrets/actions/new> by replacing the `USERNAME` with your own username.
|
||||||
|
>
|
||||||
|
> ![new_secrets_actions][new_secrets_actions]
|
||||||
|
|
||||||
- Navigate to your repo's `Settings > Secrets` and add a new secret *named* `WAKATIME_API_KEY` with your API key as it's *value*.
|
- If you're not using [profile repository][profile_readme], add another secret _named_ `GH_TOKEN` and insert your [GitHub token][gh_access_token]\* in place of _value_.
|
||||||
> Or use the url <https://github.com/USERNAME/USERNAME/settings/secrets/actions/new> by replacing the `USERNAME` with your own username.
|
|
||||||
> ![new_secrets_actions][new_secrets_actions]
|
|
||||||
|
|
||||||
- If you're not using [profile repository][profile_readme], add another secret *named* `GH_TOKEN` and insert your [GitHub token][gh_access_token]\* in place of *value*.
|
- Create a new workflow file (`waka-readme.yml`) inside `.github/workflows/` folder of your repository. You can create it from a template using the _actions tab_ of your repository too.
|
||||||
|
|
||||||
- Create a new workflow file (`waka-readme.yml`) inside `.github/workflows/` folder of your repository. You can create it from a template using the *actions tab* of your repository too.
|
|
||||||
- Clear any existing contents, add the following lines and save the file.
|
- Clear any existing contents, add the following lines and save the file.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
name: Waka Readme
|
name: Waka Readme
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: # for manual workflow trigger
|
workflow_dispatch: # for manual workflow trigger
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *' # runs at every 12AM UTC
|
- cron: "0 0 * * *" # runs at every 12AM UTC
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-readme:
|
update-readme:
|
||||||
name: WakaReadme DevMetrics
|
name: WakaReadme DevMetrics
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: athul/waka-readme@master
|
- uses: athul/waka-readme@master
|
||||||
with:
|
with:
|
||||||
WAKATIME_API_KEY: ${{ secrets.WAKATIME_API_KEY }}
|
WAKATIME_API_KEY: ${{ secrets.WAKATIME_API_KEY }}
|
||||||
# following flags are required, only if this is not on
|
# following flags are required, only if this is not on
|
||||||
# profile readme, remove the leading `#` to use them
|
# profile readme, remove the leading `#` to use them
|
||||||
#GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
#GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
#REPOSITORY: <gh_username/gh_username>
|
#REPOSITORY: <gh_username/gh_username>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tweaks
|
## Tweaks
|
||||||
|
|
||||||
@ -78,10 +82,10 @@ There are many flags that you can tweak to suit your taste!
|
|||||||
| ------------------ | -------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
| ------------------ | -------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||||
| `API_BASE_URL` | `https://wakatime.com/api` | `https://wakatime.com/api`, `https://wakapi.dev/api`, `https://hakatime.mtx-dev.xyz/api` | Integration with WakaTime compatible services like [Wakapi][wakapi] & [Hakatime][hakatime] are possible |
|
| `API_BASE_URL` | `https://wakatime.com/api` | `https://wakatime.com/api`, `https://wakapi.dev/api`, `https://hakatime.mtx-dev.xyz/api` | Integration with WakaTime compatible services like [Wakapi][wakapi] & [Hakatime][hakatime] are possible |
|
||||||
| `REPOSITORY` | `<gh_username>/<gh_username>` | `<gh_username>/<repo_name>` | Waka-readme stats will appear on the provided repository |
|
| `REPOSITORY` | `<gh_username>/<gh_username>` | `<gh_username>/<repo_name>` | Waka-readme stats will appear on the provided repository |
|
||||||
| `SECTION_NAME` | `waka` | `any string` | The generator will look for this section to fill up the readme. |
|
| `SECTION_NAME` | `waka` | Any alphanumeric string | The generator will look for this section to fill up the readme. |
|
||||||
| `COMMIT_MESSAGE` | `Updated waka-readme graph with new metrics` | anything else! | Messaged used when committing updated stats |
|
| `COMMIT_MESSAGE` | `Updated waka-readme graph with new metrics` | Any string | Messaged used when committing updated stats |
|
||||||
| `SHOW_TITLE` | `false` | `false`, `true` | Add title to waka-readme stats blob |
|
| `SHOW_TITLE` | `false` | `false`, `true` | Add title to waka-readme stats blob |
|
||||||
| `BLOCKS` | `░▒▓█` | `░▒▓█`, `⣀⣄⣤⣦⣶⣷⣿`, `-#`, you can be creative! | Ascii art used to build stats graph |
|
| `BLOCKS` | `░▒▓█` | `░▒▓█`, `⣀⣄⣤⣦⣶⣷⣿`, `-#`, you can be creative! | Ascii art used to build stats graph |
|
||||||
| `TIME_RANGE` | `last_7_days` | `last_7_days`, `last_30_days`, `last_6_months`, `last_year`, `all_time` | String representing a dispensation from which stats are aggregated |
|
| `TIME_RANGE` | `last_7_days` | `last_7_days`, `last_30_days`, `last_6_months`, `last_year`, `all_time` | String representing a dispensation from which stats are aggregated |
|
||||||
| `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 |
|
||||||
@ -98,7 +102,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
# Runs at 12am UTC
|
# Runs at 12am UTC
|
||||||
- cron: '0 0 * * *'
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-readme:
|
update-readme:
|
||||||
@ -132,14 +136,13 @@ Other 47 hrs 58 mins >------------------------ 03.05 %
|
|||||||
|
|
||||||
## Why only the language stats (and not other data) from the API?
|
## Why only the language stats (and not other data) from the API?
|
||||||
|
|
||||||
I am a fan of minimal designs and the profile readme is a great way to show off your skills and interests. The WakaTime API, gets us a **lot of data** about a person's **coding activity including the editors and Operating Systems you used and the projects you worked on**. Some of these projects maybe secretive and should not be shown out to the public. Using up more data via the Wakatime API will clutter the profile readme and hinder your chances on displaying what you provide **value to the community** like the pinned Repositories. I believe that **Coding Stats is nerdiest of all** since you can tell the community that you are ***exercising these languages or learning a new language***, this will also show that you spend some amount of time to learn and exercise your development skills. That's what matters in the end :heart:
|
I am a fan of minimal designs and the profile readme is a great way to show off your skills and interests. The WakaTime API, gets us a **lot of data** about a person's **coding activity including the editors and Operating Systems you used and the projects you worked on**. Some of these projects maybe secretive and should not be shown out to the public. Using up more data via the Wakatime API will clutter the profile readme and hinder your chances on displaying what you provide **value to the community** like the pinned Repositories. I believe that **Coding Stats is nerdiest of all** since you can tell the community that you are **_exercising these languages or learning a new language_**, this will also show that you spend some amount of time to learn and exercise your development skills. That's what matters in the end :heart:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<sup>*</sup>`REPOSITORY` flag and `GH_TOKEN` secret are required you're not using profile readme.
|
<sup>\*</sup>`REPOSITORY` flag and `GH_TOKEN` secret are required you're not using profile readme.
|
||||||
|
|
||||||
[//]: #(Links)
|
[//]: #(Links)
|
||||||
|
|
||||||
[wakapi]: https://wakapi.dev
|
[wakapi]: https://wakapi.dev
|
||||||
[hakatime]: https://github.com/mujx/hakatime
|
[hakatime]: https://github.com/mujx/hakatime
|
||||||
[waka_plugins]: https://wakatime.com/plugins
|
[waka_plugins]: https://wakatime.com/plugins
|
||||||
|
346
main.py
346
main.py
@ -54,121 +54,59 @@ from github import GithubException, Github
|
|||||||
from faker import Faker
|
from faker import Faker
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=logging-fstring-interpolation
|
# pylint: disable = logging-fstring-interpolation
|
||||||
|
|
||||||
################### data ###################
|
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
|
||||||
class WakaConstants:
|
|
||||||
"""
|
|
||||||
WakaConstants
|
|
||||||
-------------
|
|
||||||
"""
|
|
||||||
prefix_length: int = 16
|
|
||||||
graph_length: int = 25
|
|
||||||
section: str = os.getenv("INPUT_SECTION_NAME", "waka")
|
|
||||||
start_comment: str = f'<!--START_SECTION:{section}-->'
|
|
||||||
end_comment: str = f'<!--END_SECTION:{section}-->'
|
|
||||||
waka_block_pattern: str = f'{start_comment}[\\s\\S]+{end_comment}'
|
|
||||||
|
|
||||||
def validate_constants(self) -> bool:
|
|
||||||
if not (self.section):
|
|
||||||
logger.error('Invalid section name input, refer README')
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class WakaInput:
|
################### setup ###################
|
||||||
"""
|
|
||||||
WakaInput Env Vars
|
|
||||||
------------------
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
"""
|
|
||||||
WakaInput Initialize
|
|
||||||
--------------------
|
|
||||||
"""
|
|
||||||
# mapped environment variables
|
|
||||||
# # required
|
|
||||||
self.gh_token: str = os.getenv('INPUT_GH_TOKEN')
|
|
||||||
self.waka_key: str = os.getenv('INPUT_WAKATIME_API_KEY')
|
|
||||||
self.api_base_url: str = os.getenv(
|
|
||||||
'INPUT_API_BASE_URL', 'https://wakatime.com/api'
|
|
||||||
)
|
|
||||||
self.repository: str = os.getenv('INPUT_REPOSITORY')
|
|
||||||
# # depends
|
|
||||||
self.commit_message: str = os.getenv(
|
|
||||||
'INPUT_COMMIT_MESSAGE', 'Updated WakaReadme graph with new metrics'
|
|
||||||
)
|
|
||||||
# # optional
|
|
||||||
self.show_title: str | bool = os.getenv('INPUT_SHOW_TITLE', 'False')
|
|
||||||
self.block_style: str = os.getenv('INPUT_BLOCKS', '░▒▓█')
|
|
||||||
self.time_range: str = os.getenv('INPUT_TIME_RANGE', 'last_7_days')
|
|
||||||
self.show_time: str | bool = os.getenv('INPUT_SHOW_TIME', 'False')
|
|
||||||
self.show_total_time: str | bool = os.getenv(
|
|
||||||
'INPUT_SHOW_TOTAL', 'False'
|
|
||||||
)
|
|
||||||
self.show_masked_time: str | bool = os.getenv(
|
|
||||||
'INPUT_SHOW_MASKED_TIME', 'False'
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_input(self) -> bool:
|
|
||||||
"""
|
|
||||||
WakaInput Validate
|
|
||||||
------------------
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not (self.gh_token and self.waka_key and self.api_base_url and self.repository):
|
|
||||||
logger.error('Invalid required input(s), refer README')
|
|
||||||
return False
|
|
||||||
|
|
||||||
if len(self.commit_message) < 1:
|
|
||||||
logger.error(
|
|
||||||
'Commit message length must be greater than 1 character long'
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.show_title: bool = strtobool(self.show_title)
|
|
||||||
self.show_time: bool = strtobool(self.show_time)
|
|
||||||
self.show_total_time: bool = strtobool(self.show_total_time)
|
|
||||||
self.show_masked_time: bool = strtobool(self.show_masked_time)
|
|
||||||
except (ValueError, AttributeError) as err:
|
|
||||||
logger.error(err)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if len(self.block_style) < 2:
|
|
||||||
logger.warning(
|
|
||||||
'Block length should be greater than 2 characters long'
|
|
||||||
)
|
|
||||||
logger.debug('Using default blocks: ░▒▓█')
|
|
||||||
|
|
||||||
# 'all_time' is un-documented, should it be used?
|
|
||||||
if self.time_range not in {
|
|
||||||
'last_7_days', 'last_30_days', 'last_6_months', 'last_year', 'all_time'
|
|
||||||
}:
|
|
||||||
logger.warning('Invalid time range')
|
|
||||||
logger.debug('Using default time range: last_7_days')
|
|
||||||
self.time_range: str = 'last_7_days'
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def strtobool(val: str) -> bool:
|
print()
|
||||||
|
# hush existing loggers
|
||||||
|
# pylint: disable = no-member # see: https://stackoverflow.com/q/20965287
|
||||||
|
for lgr_name in logger.root.manager.loggerDict:
|
||||||
|
# to disable log propagation completely set '.propagate = False'
|
||||||
|
logger.getLogger(lgr_name).setLevel(logger.WARNING)
|
||||||
|
# pylint: enable = no-member
|
||||||
|
# somehow github.Requester gets missed out from loggerDict
|
||||||
|
logger.getLogger('github.Requester').setLevel(logger.WARNING)
|
||||||
|
# configure logger
|
||||||
|
logger.basicConfig(
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S',
|
||||||
|
format='[%(asctime)s] ln. %(lineno)-3d %(levelname)-8s %(message)s',
|
||||||
|
level=logger.DEBUG
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
if len(sys.argv) == 2 and sys.argv[1] == '--dev':
|
||||||
|
# get env-vars from .env file for development
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
# comment this out to disable colored logging
|
||||||
|
from loguru import logger
|
||||||
|
# load from .env before class def gets parsed
|
||||||
|
load_dotenv()
|
||||||
|
except ImportError as im_err:
|
||||||
|
logger.warning(im_err)
|
||||||
|
|
||||||
|
|
||||||
|
################### lib-func ###################
|
||||||
|
|
||||||
|
|
||||||
|
def strtobool(val: str | bool) -> bool:
|
||||||
"""
|
"""
|
||||||
strtobool
|
strtobool
|
||||||
---------
|
---------
|
||||||
|
|
||||||
PEP 632 https://www.python.org/dev/peps/pep-0632/ is depreciating distutils
|
PEP 632 https://www.python.org/dev/peps/pep-0632/ is depreciating distutils.
|
||||||
|
This is from the official source code with slight modifications.
|
||||||
|
|
||||||
Following code is somewhat shamelessly copied from the original source.
|
Converts a string representation of truth to True or False.
|
||||||
|
|
||||||
Convert a string representation of truth to True or False.
|
|
||||||
|
|
||||||
- True values are `'y', 'yes', 't', 'true', 'on', and '1'`
|
- True values are `'y', 'yes', 't', 'true', 'on', and '1'`
|
||||||
- False values are `'n', 'no', 'f', 'false', 'off', and '0'`
|
- False values are `'n', 'no', 'f', 'false', 'off', and '0'`
|
||||||
- Raises `ValueError` if `val` is anything else.
|
- Raises `ValueError` if `val` is anything else.
|
||||||
"""
|
"""
|
||||||
|
if isinstance(val, bool):
|
||||||
|
return val
|
||||||
|
|
||||||
val = val.lower()
|
val = val.lower()
|
||||||
|
|
||||||
if val in {'y', 'yes', 't', 'true', 'on', '1'}:
|
if val in {'y', 'yes', 't', 'true', 'on', '1'}:
|
||||||
@ -180,9 +118,97 @@ def strtobool(val: str) -> bool:
|
|||||||
raise ValueError(f'invalid truth value for {val}')
|
raise ValueError(f'invalid truth value for {val}')
|
||||||
|
|
||||||
|
|
||||||
|
################### data ###################
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True)
|
||||||
|
class WakaInput:
|
||||||
|
"""
|
||||||
|
WakaReadme Input Env Variables
|
||||||
|
------------------------------
|
||||||
|
"""
|
||||||
|
# constants
|
||||||
|
prefix_length: int = 16
|
||||||
|
graph_length: int = 25
|
||||||
|
|
||||||
|
# mapped environment variables
|
||||||
|
# # required
|
||||||
|
gh_token: str | None = os.getenv('INPUT_GH_TOKEN')
|
||||||
|
waka_key: str | None = os.getenv('INPUT_WAKATIME_API_KEY')
|
||||||
|
api_base_url: str | None = os.getenv(
|
||||||
|
'INPUT_API_BASE_URL', 'https://wakatime.com/api'
|
||||||
|
)
|
||||||
|
repository: str | None = os.getenv('INPUT_REPOSITORY')
|
||||||
|
# # depends
|
||||||
|
commit_message: str = os.getenv(
|
||||||
|
'INPUT_COMMIT_MESSAGE', 'Updated WakaReadme graph with new metrics'
|
||||||
|
)
|
||||||
|
_section_name: str = os.getenv('INPUT_SECTION_NAME', 'waka')
|
||||||
|
start_comment: str = f'<!--START_SECTION:{_section_name}-->'
|
||||||
|
end_comment: str = f'<!--END_SECTION:{_section_name}-->'
|
||||||
|
waka_block_pattern: str = f'{start_comment}[\\s\\S]+{end_comment}'
|
||||||
|
# # optional
|
||||||
|
show_title: str | bool = os.getenv('INPUT_SHOW_TITLE') or False
|
||||||
|
block_style: str = os.getenv('INPUT_BLOCKS', '░▒▓█')
|
||||||
|
time_range: str = os.getenv('INPUT_TIME_RANGE', 'last_7_days')
|
||||||
|
show_time: str | bool = os.getenv('INPUT_SHOW_TIME') 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
|
||||||
|
|
||||||
|
def validate_input(self) -> bool:
|
||||||
|
"""
|
||||||
|
Validate Input Env Variables
|
||||||
|
----------------------------
|
||||||
|
"""
|
||||||
|
logger.debug('Validating input variables')
|
||||||
|
if not self.gh_token or not self.waka_key or not self.api_base_url or not self.repository:
|
||||||
|
logger.error('Invalid inputs')
|
||||||
|
logger.info('Refer https://github.com/athul/waka-readme')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(self.commit_message) < 1:
|
||||||
|
logger.error(
|
||||||
|
'Commit message length must be greater than 1 character long'
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.show_title = strtobool(self.show_title)
|
||||||
|
self.show_time = strtobool(self.show_time)
|
||||||
|
self.show_total_time = strtobool(self.show_total_time)
|
||||||
|
self.show_masked_time = strtobool(self.show_masked_time)
|
||||||
|
except ValueError as err:
|
||||||
|
logger.error(err)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._section_name.isalnum():
|
||||||
|
logger.warning('Section name must be in any of [[a-z][A-Z][0-9]]')
|
||||||
|
logger.debug('Using default section name: waka')
|
||||||
|
self._section_name = 'waka'
|
||||||
|
self.start_comment = f'<!--START_SECTION:{self._section_name}-->'
|
||||||
|
self.end_comment = f'<!--END_SECTION:{self._section_name}-->'
|
||||||
|
self.waka_block_pattern = f'{self.start_comment}[\\s\\S]+{self.end_comment}'
|
||||||
|
|
||||||
|
if len(self.block_style) < 2:
|
||||||
|
logger.warning('Graph block must be longer than 2 characters')
|
||||||
|
logger.debug('Using default blocks: ░▒▓█')
|
||||||
|
self.block_style = '░▒▓█'
|
||||||
|
|
||||||
|
if self.time_range not in {
|
||||||
|
'last_7_days', 'last_30_days', 'last_6_months', 'last_year', 'all_time'
|
||||||
|
}: # 'all_time' is un-documented, should it be used?
|
||||||
|
logger.warning('Invalid time range')
|
||||||
|
logger.debug('Using default time range: last_7_days')
|
||||||
|
self.time_range = 'last_7_days'
|
||||||
|
|
||||||
|
logger.debug('Input validation complete\n')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
################### logic ###################
|
################### logic ###################
|
||||||
|
|
||||||
def make_title(dawn: str, dusk: str, /) -> str:
|
|
||||||
|
def make_title(dawn: str | None, dusk: str | None, /) -> str:
|
||||||
"""
|
"""
|
||||||
WakaReadme Title
|
WakaReadme Title
|
||||||
----------------
|
----------------
|
||||||
@ -190,24 +216,22 @@ def make_title(dawn: str, dusk: str, /) -> str:
|
|||||||
Makes title for WakaReadme.
|
Makes title for WakaReadme.
|
||||||
"""
|
"""
|
||||||
logger.debug('Making title')
|
logger.debug('Making title')
|
||||||
if not (dawn or dusk):
|
if not dawn or not dusk:
|
||||||
logger.error('Cannot find start/end date')
|
logger.error('Cannot find start/end date\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
api_dfm, msg_dfm = '%Y-%m-%dT%H:%M:%SZ', '%d %B %Y'
|
api_dfm, msg_dfm = '%Y-%m-%dT%H:%M:%SZ', '%d %B %Y'
|
||||||
try:
|
try:
|
||||||
start_date = datetime.strptime(dawn, api_dfm).strftime(msg_dfm)
|
start_date = datetime.strptime(dawn, api_dfm).strftime(msg_dfm)
|
||||||
end_date = datetime.strptime(dusk, api_dfm).strftime(msg_dfm)
|
end_date = datetime.strptime(dusk, api_dfm).strftime(msg_dfm)
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
logger.error(err)
|
logger.error(f'{err}\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
logger.debug('Title was made\n')
|
logger.debug('Title was made\n')
|
||||||
return f'From: {start_date} - To: {end_date}'
|
return f'From: {start_date} - To: {end_date}'
|
||||||
|
|
||||||
|
|
||||||
def make_graph(
|
def make_graph(block_style: str, percent: float, gr_len: int, lg_nm: str = '', /) -> str:
|
||||||
block_style: str, percent: float, gr_len: str, /,
|
|
||||||
*, lg_nm: str = ''
|
|
||||||
) -> str:
|
|
||||||
"""
|
"""
|
||||||
WakaReadme Graph
|
WakaReadme Graph
|
||||||
----------------
|
----------------
|
||||||
@ -215,19 +239,20 @@ def make_graph(
|
|||||||
Makes time graph from the API's data.
|
Makes time graph from the API's data.
|
||||||
"""
|
"""
|
||||||
logger.debug(f'Generating graph for "{lg_nm or "..."}"')
|
logger.debug(f'Generating graph for "{lg_nm or "..."}"')
|
||||||
markers: int = len(block_style) - 1
|
markers = len(block_style) - 1
|
||||||
proportion: float = percent / 100 * gr_len
|
proportion = percent / 100 * gr_len
|
||||||
graph_bar: str = block_style[-1] * int(proportion + 0.5 / markers)
|
graph_bar = block_style[-1] * int(proportion + 0.5 / markers)
|
||||||
remainder_block: int = int(
|
remainder_block = int(
|
||||||
(proportion - len(graph_bar)) * markers + 0.5
|
(proportion - len(graph_bar)) * markers + 0.5
|
||||||
)
|
)
|
||||||
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 | None, /) -> str:
|
def prep_content(stats: dict[Any, Any], /) -> str:
|
||||||
"""
|
"""
|
||||||
WakaReadme Prepare Markdown
|
WakaReadme Prepare Markdown
|
||||||
---------------------------
|
---------------------------
|
||||||
@ -235,7 +260,7 @@ def prep_content(stats: dict | None, /) -> str:
|
|||||||
Prepared markdown content from the fetched statistics
|
Prepared markdown content from the fetched statistics
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
contents: str = ''
|
contents = ''
|
||||||
|
|
||||||
# Check if any data exists
|
# Check if any data exists
|
||||||
if not (lang_info := stats.get('languages')):
|
if not (lang_info := stats.get('languages')):
|
||||||
@ -262,18 +287,17 @@ def prep_content(stats: dict | None, /) -> str:
|
|||||||
logger.debug('Making contents')
|
logger.debug('Making contents')
|
||||||
pad_len = len(
|
pad_len = len(
|
||||||
# comment if it feels way computationally expensive
|
# comment if it feels way computationally expensive
|
||||||
max((str(l.get('name')) for l in lang_info), key=len)
|
max((l.get('name') for l 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: str = lang.get('name')
|
lang_name = lang.get('name')
|
||||||
# >>> add languages to filter here <<<
|
# >>> add languages to filter here <<<
|
||||||
# if lang_name in {...}: continue
|
# if lang_name in {...}: continue
|
||||||
lang_time: str = lang.get('text') if wk_i.show_time else ''
|
lang_time = lang.get('text') if wk_i.show_time else ''
|
||||||
lang_ratio: float = lang.get('percent')
|
lang_ratio = lang.get('percent')
|
||||||
lang_bar: str = make_graph(
|
lang_bar = make_graph(
|
||||||
wk_i.block_style, lang_ratio, wk_c.graph_length,
|
wk_i.block_style, lang_ratio, wk_i.graph_length, lang_name
|
||||||
lg_nm=lang_name
|
|
||||||
)
|
)
|
||||||
contents += (
|
contents += (
|
||||||
f'{lang_name.ljust(pad_len)} ' +
|
f'{lang_name.ljust(pad_len)} ' +
|
||||||
@ -287,34 +311,39 @@ def prep_content(stats: dict | None, /) -> str:
|
|||||||
return contents.rstrip('\n')
|
return contents.rstrip('\n')
|
||||||
|
|
||||||
|
|
||||||
def fetch_stats() -> Any:
|
def fetch_stats() -> dict[Any, Any] | None:
|
||||||
"""
|
"""
|
||||||
WakaReadme Fetch Stats
|
WakaReadme Fetch Stats
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Returns statistics as JSON string
|
Returns statistics as JSON string
|
||||||
"""
|
"""
|
||||||
attempts, statistic = 4, {}
|
attempts = 4
|
||||||
encoded_key: str = str(b64encode(bytes(wk_i.waka_key, 'utf-8')), 'utf-8')
|
statistic: dict[str, dict[Any, Any]] = {}
|
||||||
|
encoded_key = str(
|
||||||
|
b64encode(bytes(str(wk_i.waka_key), 'utf-8')), 'utf-8'
|
||||||
|
)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'Pulling WakaTime stats from {" ".join(wk_i.time_range.split("_"))}'
|
f'Pulling WakaTime stats from {" ".join(wk_i.time_range.split("_"))}'
|
||||||
)
|
)
|
||||||
|
|
||||||
while attempts > 0:
|
while attempts > 0:
|
||||||
resp_message, fake_ua = None, cryptogenic.choice(
|
resp_message, fake_ua = '', cryptogenic.choice(
|
||||||
[str(fake.user_agent()) for _ in range(5)]
|
[str(fake.user_agent()) for _ in range(5)]
|
||||||
)
|
)
|
||||||
# making a request
|
# making a request
|
||||||
if (resp := rq_get(
|
if (resp := rq_get(
|
||||||
url=f'{wk_i.api_base_url.rstrip("/")}/v1/users/current/stats/{wk_i.time_range}',
|
url=f'{str(wk_i.api_base_url).rstrip("/")}/v1/users/current/stats/{wk_i.time_range}',
|
||||||
headers={
|
headers={
|
||||||
'Authorization': f'Basic {encoded_key}',
|
'Authorization': f'Basic {encoded_key}',
|
||||||
'User-Agent': fake_ua,
|
'User-Agent': fake_ua,
|
||||||
},
|
},
|
||||||
|
timeout=30 * (5 - attempts)
|
||||||
)).status_code != 200:
|
)).status_code != 200:
|
||||||
resp_message = f'• {resp.json().get("message")}'
|
resp_message += f' • {conn_info}' if (
|
||||||
|
conn_info := resp.json().get("message")
|
||||||
|
) else ''
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'API response #{5 - attempts}: {resp.status_code} • {resp.reason} {resp_message or ""}'
|
f'API response #{5 - attempts}: {resp.status_code} • {resp.reason}{resp_message}'
|
||||||
)
|
)
|
||||||
if resp.status_code == 200 and (statistic := resp.json()):
|
if resp.status_code == 200 and (statistic := resp.json()):
|
||||||
logger.debug('Fetched WakaTime statistics')
|
logger.debug('Fetched WakaTime statistics')
|
||||||
@ -324,7 +353,7 @@ def fetch_stats() -> Any:
|
|||||||
attempts -= 1
|
attempts -= 1
|
||||||
|
|
||||||
if err := (statistic.get('error') or statistic.get('errors')):
|
if err := (statistic.get('error') or statistic.get('errors')):
|
||||||
logger.error(err)
|
logger.error(f'{err}\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print()
|
print()
|
||||||
@ -340,15 +369,18 @@ def churn(old_readme: str, /) -> str | None:
|
|||||||
"""
|
"""
|
||||||
# getting content
|
# getting content
|
||||||
if not (waka_stats := fetch_stats()):
|
if not (waka_stats := fetch_stats()):
|
||||||
logger.error('Unable to fetch data, please rerun workflow')
|
logger.error('Unable to fetch data, please rerun workflow\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# processing content
|
# processing content
|
||||||
generated_content = prep_content(waka_stats)
|
try:
|
||||||
|
generated_content = prep_content(waka_stats)
|
||||||
|
except AttributeError as err:
|
||||||
|
logger.error(f'Unable to read API data | {err}\n')
|
||||||
|
sys.exit(1)
|
||||||
print(generated_content, '\n', sep='')
|
print(generated_content, '\n', sep='')
|
||||||
new_readme = re.sub(
|
new_readme = re.sub(
|
||||||
pattern=wk_c.waka_block_pattern,
|
pattern=wk_i.waka_block_pattern,
|
||||||
repl=f'{wk_c.start_comment}\n\n```text\n{generated_content}\n```\n\n{wk_c.end_comment}',
|
repl=f'{wk_i.start_comment}\n\n```text\n{generated_content}\n```\n\n{wk_i.end_comment}',
|
||||||
string=old_readme
|
string=old_readme
|
||||||
)
|
)
|
||||||
if len(sys.argv) == 2 and sys.argv[1] == '--dev':
|
if len(sys.argv) == 2 and sys.argv[1] == '--dev':
|
||||||
@ -356,7 +388,7 @@ def churn(old_readme: str, /) -> str | None:
|
|||||||
# to avoid accidentally writing back to Github
|
# to avoid accidentally writing back to Github
|
||||||
# when developing and testing WakaReadme
|
# when developing and testing WakaReadme
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return None if new_readme == old_readme else new_readme
|
return None if new_readme == old_readme else new_readme
|
||||||
|
|
||||||
|
|
||||||
@ -364,7 +396,8 @@ def genesis() -> None:
|
|||||||
"""Run Program"""
|
"""Run Program"""
|
||||||
logger.debug('Connecting to GitHub')
|
logger.debug('Connecting to GitHub')
|
||||||
gh_connect = Github(wk_i.gh_token)
|
gh_connect = Github(wk_i.gh_token)
|
||||||
gh_repo = gh_connect.get_repo(wk_i.repository)
|
# since a validator is being used casting to string here is okay
|
||||||
|
gh_repo = gh_connect.get_repo(str(wk_i.repository))
|
||||||
readme_file = gh_repo.get_readme()
|
readme_file = gh_repo.get_readme()
|
||||||
logger.debug('Decoding readme contents\n')
|
logger.debug('Decoding readme contents\n')
|
||||||
readme_contents = str(readme_file.decoded_content, encoding='utf-8')
|
readme_contents = str(readme_file.decoded_content, encoding='utf-8')
|
||||||
@ -378,34 +411,11 @@ def genesis() -> None:
|
|||||||
)
|
)
|
||||||
logger.info('Stats updated successfully')
|
logger.info('Stats updated successfully')
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info('WakaReadme was not updated')
|
logger.info('WakaReadme was not updated')
|
||||||
|
|
||||||
|
|
||||||
################### driver ###################
|
################### driver ###################
|
||||||
print()
|
|
||||||
# hush existing loggers
|
|
||||||
# pylint: disable = no-member # see: https://stackoverflow.com/q/20965287
|
|
||||||
for lgr_name in logger.root.manager.loggerDict:
|
|
||||||
# to disable log propagation completely set '.propagate = False'
|
|
||||||
logger.getLogger(lgr_name).setLevel(logger.WARNING)
|
|
||||||
# pylint: enable = no-member
|
|
||||||
# somehow github.Requester gets missed out from loggerDict
|
|
||||||
logger.getLogger('github.Requester').setLevel(logger.WARNING)
|
|
||||||
# configure logger
|
|
||||||
logger.basicConfig(
|
|
||||||
datefmt='%Y-%m-%d %H:%M:%S',
|
|
||||||
format='[%(asctime)s] ln. %(lineno)-3d %(levelname)-8s %(message)s',
|
|
||||||
level=logger.DEBUG
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
if len(sys.argv) == 2 and sys.argv[1] == '--dev':
|
|
||||||
# get env-vars from .env file for development
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
# comment this out to disable colored logging
|
|
||||||
from loguru import logger
|
|
||||||
load_dotenv()
|
|
||||||
except ImportError as im_err:
|
|
||||||
logger.warning(im_err)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@ -416,21 +426,19 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# initial waka-readme setup
|
# initial waka-readme setup
|
||||||
logger.debug('Initialize WakaReadme')
|
logger.debug('Initialize WakaReadme')
|
||||||
wk_c = WakaConstants()
|
|
||||||
wk_i = WakaInput()
|
wk_i = WakaInput()
|
||||||
if not (wk_i.validate_input() or wk_i.validate_constants()):
|
if not wk_i.validate_input():
|
||||||
logger.error('Environment variables are misconfigured')
|
logger.error('Environment variables are misconfigured\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
logger.debug('Input validation complete')
|
|
||||||
|
|
||||||
# run
|
# run
|
||||||
try:
|
try:
|
||||||
genesis()
|
genesis()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print()
|
print()
|
||||||
logger.error('Interrupt signal received')
|
logger.error('Interrupt signal received\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except (GithubException, RequestException) as rq_exp:
|
except (GithubException, RequestException) as rq_exp:
|
||||||
logger.critical(rq_exp)
|
logger.critical(f'{rq_exp}\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
print('\nThanks for using WakaReadme!')
|
print('\nThanks for using WakaReadme!\n')
|
||||||
|
38
poetry.lock
generated
38
poetry.lock
generated
@ -106,7 +106,7 @@ graph = ["objgraph (>=1.7.2)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "faker"
|
name = "faker"
|
||||||
version = "15.3.2"
|
version = "15.3.3"
|
||||||
description = "Faker is a Python package that generates fake data for you."
|
description = "Faker is a Python package that generates fake data for you."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@ -117,11 +117,11 @@ python-dateutil = ">=2.4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gitdb"
|
name = "gitdb"
|
||||||
version = "4.0.9"
|
version = "4.0.10"
|
||||||
description = "Git Object Database"
|
description = "Git Object Database"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
smmap = ">=3.0.1,<6"
|
smmap = ">=3.0.1,<6"
|
||||||
@ -212,7 +212,7 @@ test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pycodestyle"
|
name = "pycodestyle"
|
||||||
version = "2.9.1"
|
version = "2.10.0"
|
||||||
description = "Python style guide checker"
|
description = "Python style guide checker"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@ -259,14 +259,14 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pylint"
|
name = "pylint"
|
||||||
version = "2.15.6"
|
version = "2.15.7"
|
||||||
description = "python code static checker"
|
description = "python code static checker"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7.2"
|
python-versions = ">=3.7.2"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
astroid = ">=2.12.12,<=2.14.0-dev0"
|
astroid = ">=2.12.13,<=2.14.0-dev0"
|
||||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||||
dill = ">=0.2"
|
dill = ">=0.2"
|
||||||
isort = ">=4.2.5,<6"
|
isort = ">=4.2.5,<6"
|
||||||
@ -386,11 +386,11 @@ python-versions = ">=3.6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "1.26.12"
|
version = "1.26.13"
|
||||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
|
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
|
||||||
@ -419,7 +419,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.11"
|
python-versions = "^3.11"
|
||||||
content-hash = "ad776661cb1c47292702d2f54f39a1bdc550fc685a24ef895f5c271fd1a2b1a7"
|
content-hash = "89c4152cefcfa8432ed877f828bf63524947fe9a95159f28cada0f9dc9397616"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
astroid = [
|
astroid = [
|
||||||
@ -521,12 +521,12 @@ dill = [
|
|||||||
{file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"},
|
{file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"},
|
||||||
]
|
]
|
||||||
faker = [
|
faker = [
|
||||||
{file = "Faker-15.3.2-py3-none-any.whl", hash = "sha256:43da04aae745018e8bded768e74c84423d9dc38e4c498a53439e749d90e20bc0"},
|
{file = "Faker-15.3.3-py3-none-any.whl", hash = "sha256:b95b2423ef18d17dcd5977732a0bf0fbbde4937f10dce24ff804581f7f3ca4e9"},
|
||||||
{file = "Faker-15.3.2.tar.gz", hash = "sha256:0094fe3340ad73c490d3ffccc59cc171b161acfccccd52925c70970ba23e6d6b"},
|
{file = "Faker-15.3.3.tar.gz", hash = "sha256:20d090e661bbe88a5d801ea5eb3d853564940352120c84c9a14968847aca2893"},
|
||||||
]
|
]
|
||||||
gitdb = [
|
gitdb = [
|
||||||
{file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
|
{file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"},
|
||||||
{file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
|
{file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"},
|
||||||
]
|
]
|
||||||
gitpython = [
|
gitpython = [
|
||||||
{file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"},
|
{file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"},
|
||||||
@ -578,8 +578,8 @@ platformdirs = [
|
|||||||
{file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"},
|
{file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"},
|
||||||
]
|
]
|
||||||
pycodestyle = [
|
pycodestyle = [
|
||||||
{file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
|
{file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"},
|
||||||
{file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
|
{file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"},
|
||||||
]
|
]
|
||||||
pycparser = [
|
pycparser = [
|
||||||
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
||||||
@ -594,8 +594,8 @@ pyjwt = [
|
|||||||
{file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"},
|
{file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"},
|
||||||
]
|
]
|
||||||
pylint = [
|
pylint = [
|
||||||
{file = "pylint-2.15.6-py3-none-any.whl", hash = "sha256:15060cc22ed6830a4049cf40bc24977744df2e554d38da1b2657591de5bcd052"},
|
{file = "pylint-2.15.7-py3-none-any.whl", hash = "sha256:1d561d1d3e8be9dd880edc685162fbdaa0409c88b9b7400873c0cf345602e326"},
|
||||||
{file = "pylint-2.15.6.tar.gz", hash = "sha256:25b13ddcf5af7d112cf96935e21806c1da60e676f952efb650130f2a4483421c"},
|
{file = "pylint-2.15.7.tar.gz", hash = "sha256:91e4776dbcb4b4d921a3e4b6fec669551107ba11f29d9199154a01622e460a57"},
|
||||||
]
|
]
|
||||||
pynacl = [
|
pynacl = [
|
||||||
{file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
|
{file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
|
||||||
@ -684,8 +684,8 @@ tomlkit = [
|
|||||||
{file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"},
|
{file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"},
|
||||||
]
|
]
|
||||||
urllib3 = [
|
urllib3 = [
|
||||||
{file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
|
{file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"},
|
||||||
{file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
|
{file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"},
|
||||||
]
|
]
|
||||||
win32-setctime = [
|
win32-setctime = [
|
||||||
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
||||||
|
@ -11,11 +11,11 @@ readme = "README.md"
|
|||||||
python = "^3.11"
|
python = "^3.11"
|
||||||
requests = "^2.28.1"
|
requests = "^2.28.1"
|
||||||
PyGithub = "^1.57"
|
PyGithub = "^1.57"
|
||||||
Faker = "^15.3.2"
|
Faker = "^15.3.3"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
autopep8 = "^2.0.0"
|
autopep8 = "^2.0.0"
|
||||||
pylint = "^2.15.6"
|
pylint = "^2.15.7"
|
||||||
python-dotenv = "^0.21.0"
|
python-dotenv = "^0.21.0"
|
||||||
loguru = "^0.6.0"
|
loguru = "^0.6.0"
|
||||||
bandit = "^1.7.4"
|
bandit = "^1.7.4"
|
||||||
|
@ -25,9 +25,9 @@ class TestData:
|
|||||||
"""Test Data"""
|
"""Test Data"""
|
||||||
# for future tests
|
# for future tests
|
||||||
# waka_json: dict | None = None
|
# waka_json: dict | None = None
|
||||||
bar_percent: tuple[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
|
||||||
dummy_readme: str = ''
|
dummy_readme: str = ''
|
||||||
|
|
||||||
def populate(self) -> None:
|
def populate(self) -> None:
|
||||||
@ -98,6 +98,9 @@ class TestMain(unittest.TestCase):
|
|||||||
|
|
||||||
def test_make_graph(self) -> None:
|
def test_make_graph(self) -> None:
|
||||||
"""Test graph maker"""
|
"""Test graph maker"""
|
||||||
|
if not tds.graph_blocks or not tds.waka_graphs or not tds.bar_percent:
|
||||||
|
raise AssertionError('Data population failed')
|
||||||
|
|
||||||
for (idx, grb), (jdy, bpc) in product(
|
for (idx, grb), (jdy, bpc) in product(
|
||||||
enumerate(tds.graph_blocks), enumerate(tds.bar_percent)
|
enumerate(tds.graph_blocks), enumerate(tds.bar_percent)
|
||||||
):
|
):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user