├── .flake8 ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── test-local.yml │ └── versioning.yml ├── .gitignore ├── .mypy.ini ├── Dockerfile ├── LICENSE ├── README.md ├── action.yml ├── docker-compose.yml ├── poetry.lock ├── pyproject.toml ├── src └── advent_readme_stars │ ├── __init__.py │ ├── __main__.py │ ├── advent.py │ ├── constants.py │ ├── progress.py │ └── update.py └── tests ├── __init__.py ├── conftest.py ├── data ├── example1.json ├── example2.json └── example3.json ├── helpers.py ├── test_advent.py ├── test_progress.py └── test_update.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | extend-ignore = E203, W503 4 | per-file-ignores = __init__.py:F401 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: k2bddev 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | strategy: 7 | matrix: 8 | os: [ubuntu-latest, macos-latest, windows-latest] 9 | python-version: ["3.8", "3.9", "3.10", "3.11"] 10 | 11 | runs-on: ${{ matrix.os }} 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-python@v2 16 | with: 17 | python-version: ${{ matrix.python-version }} 18 | - name: Run image 19 | uses: abatilo/actions-poetry@v2.0.0 20 | with: 21 | poetry-version: 1.5.1 22 | - name: Install project 23 | run: poetry install 24 | - name: Lint 25 | run: poetry run poe lint 26 | - name: Test 27 | run: poetry run poe test 28 | - name: Upload coverage report 29 | uses: codecov/codecov-action@v1 30 | with: 31 | token: ${{ secrets.CODECOV_TOKEN }} 32 | file: ./coverage.xml 33 | fail_ci_if_error: true 34 | -------------------------------------------------------------------------------- /.github/workflows/test-local.yml: -------------------------------------------------------------------------------- 1 | name: Test action from local ⭐ 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | update-readme: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | contents: write 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: ./ 13 | with: 14 | userId: 1511815 15 | sessionCookie: ${{ secrets.AOC_SESSION }} 16 | headerPrefix: "###" 17 | tableMarker: "" 18 | year: 2022 19 | - uses: stefanzweifel/git-auto-commit-action@v5 20 | with: 21 | commit_message: Update README stars 22 | -------------------------------------------------------------------------------- /.github/workflows/versioning.yml: -------------------------------------------------------------------------------- 1 | name: Keep the versions up-to-date 2 | 3 | on: 4 | release: 5 | types: [published, edited] 6 | 7 | jobs: 8 | actions-tagger: 9 | runs-on: windows-latest 10 | steps: 11 | - uses: Actions-R-Us/actions-tagger@latest 12 | with: 13 | publish_latest_tag: true 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .serverless 3 | 4 | .vscode/ 5 | .devcontainer 6 | 7 | *.DS_Store 8 | 9 | # Byte-compiled / optimized / DLL files 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | wheels/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .nox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | cover/ 61 | 62 | # Translations 63 | *.mo 64 | *.pot 65 | 66 | # Django stuff: 67 | *.log 68 | local_settings.py 69 | db.sqlite3 70 | db.sqlite3-journal 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | .pybuilder/ 84 | target/ 85 | 86 | # Jupyter Notebook 87 | .ipynb_checkpoints 88 | 89 | # IPython 90 | profile_default/ 91 | ipython_config.py 92 | 93 | # pyenv 94 | # For a library or package, you might want to ignore these files since the code is 95 | # intended to run in multiple environments; otherwise, check them in: 96 | # .python-version 97 | 98 | # pipenv 99 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 100 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 101 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 102 | # install all needed dependencies. 103 | #Pipfile.lock 104 | 105 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 106 | __pypackages__/ 107 | 108 | # Celery stuff 109 | celerybeat-schedule 110 | celerybeat.pid 111 | 112 | # SageMath parsed files 113 | *.sage.py 114 | 115 | # Environments 116 | .env 117 | .venv 118 | env/ 119 | venv/ 120 | ENV/ 121 | env.bak/ 122 | venv.bak/ 123 | 124 | # Spyder project settings 125 | .spyderproject 126 | .spyproject 127 | 128 | # Rope project settings 129 | .ropeproject 130 | 131 | # mkdocs documentation 132 | /site 133 | 134 | # mypy 135 | .mypy_cache/ 136 | .dmypy.json 137 | dmypy.json 138 | 139 | # Pyre type checker 140 | .pyre/ 141 | 142 | # pytype static type analyzer 143 | .pytype/ 144 | 145 | # Cython debug symbols 146 | cython_debug/ 147 | -------------------------------------------------------------------------------- /.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | ignore_missing_imports = True 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | ENV PYTHONFAULTHANDLER=1 \ 4 | PYTHONHASHSEED=random \ 5 | PYTHONUNBUFFERED=1 6 | 7 | COPY src . 8 | COPY pyproject.toml . 9 | 10 | RUN pip install -e . 11 | 12 | CMD ["python", "-m", "advent_readme_stars"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Kevin Kelsey Brian Duff (k2bd) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advent README Stars 2 | 3 | [![CI](https://github.com/k2bd/advent-readme-stars/actions/workflows/ci.yml/badge.svg)](https://github.com/k2bd/advent-readme-stars/actions/workflows/ci.yml) 4 | [![codecov](https://codecov.io/gh/k2bd/advent-readme-stars/branch/main/graph/badge.svg?token=J5FA3JU4OI)](https://codecov.io/gh/k2bd/advent-readme-stars) 5 | 6 | This [action](https://github.com/marketplace/actions/advent-readme-stars) adds and maintains a stars report in your README based on your [Advent of Code](https://adventofcode.com/) progress. 7 | 8 | ## Example Table 9 | 10 | 11 | ### 2022 Results 12 | 13 | | Day | Part 1 | Part 2 | 14 | | :---: | :---: | :---: | 15 | | [Day 1](https://adventofcode.com/2022/day/1) | ⭐ | ⭐ | 16 | | [Day 2](https://adventofcode.com/2022/day/2) | ⭐ | ⭐ | 17 | | [Day 3](https://adventofcode.com/2022/day/3) | ⭐ | ⭐ | 18 | | [Day 4](https://adventofcode.com/2022/day/4) | ⭐ | ⭐ | 19 | | [Day 5](https://adventofcode.com/2022/day/5) | ⭐ | ⭐ | 20 | | [Day 6](https://adventofcode.com/2022/day/6) | ⭐ | ⭐ | 21 | | [Day 7](https://adventofcode.com/2022/day/7) | ⭐ | ⭐ | 22 | | [Day 8](https://adventofcode.com/2022/day/8) | ⭐ | ⭐ | 23 | | [Day 9](https://adventofcode.com/2022/day/9) | ⭐ | ⭐ | 24 | | [Day 10](https://adventofcode.com/2022/day/10) | ⭐ | ⭐ | 25 | 26 | 27 | ## Quickstart 28 | 29 | Add this line somewhere in your `README.md`: 30 | 31 | ```markdown 32 | 33 | ``` 34 | 35 | Make a note of your user ID and add your session cookie to your repo as a secret called `AOC_SESSION`. 36 | To see how to find these values, see those sections in the spec below. 37 | 38 | Add this action to your repo as `.github/workflows/readme-stars.yml`, pasting in your user ID and the leaderboard ID to pull data from (or remove this argument to default to your own private leaderboard): 39 | 40 | ```yml 41 | name: Update README ⭐ 42 | on: 43 | schedule: 44 | - cron: "51 */4 * * *" # Every 4 hours 45 | workflow_dispatch: 46 | 47 | jobs: 48 | update-readme: 49 | runs-on: ubuntu-latest 50 | permissions: 51 | contents: write 52 | steps: 53 | - uses: actions/checkout@v3 54 | - uses: k2bd/advent-readme-stars@v1 55 | with: 56 | userId: 1234567 57 | leaderboardId: 9876543 58 | sessionCookie: ${{ secrets.AOC_SESSION }} 59 | - uses: stefanzweifel/git-auto-commit-action@v5 # use v5 60 | with: 61 | commit_message: Update README stars 62 | ``` 63 | 64 | If you want to adjust the cron expression, please remember to schedule your jobs such that you respect the Advent of Code request of running automated requests at a rate of no more than 1 per 15 minutes. 65 | 66 | ## Action Spec 67 | 68 | ### `userId` 69 | 70 | **Required** 71 | 72 | Your Advent of Code user ID. 73 | To get this, go to your Go to [settings](https://adventofcode.com/2021/settings). 74 | The user ID is displayed in the first option of the question "What would you like to be called?": 75 | 76 | ``` 77 | ( ) (anonymous user #) 78 | ( ) .... 79 | ``` 80 | 81 | 82 | ### `sessionCookie` 83 | 84 | **Required** 85 | 86 | Your Advent of Code session cookie. 87 | To get this, press F12 anywhere on the Advent of Code website to open your browser developer tools. 88 | Look in your Cookies under the Application or Storage tab, and copy out the session cookie. 89 | This should be stored as a repository secret, not pasted directly into the action or any other publicly viewable place. 90 | 91 | ### `leaderboardId` 92 | 93 | *Optional* - default `userId` value 94 | 95 | Your Advent of Code leaderboard ID. 96 | To get this, go to your Go to [leaderboard](https://adventofcode.com/2020/leaderboard/private) and press 'View' (or 'Create' if you haven't yet created a private leaderboard). 97 | The leaderboard ID is at the end of the URL: 98 | 99 | ``` 100 | https://adventofcode.com/2021/leaderboard/private/view/(leaderboard ID) 101 | ``` 102 | 103 | ### `tableMarker` 104 | 105 | *Optional* - default `` 106 | 107 | This is the string that marks the table location in your README file. The action will only work if it finds this marker in your file, on its own line. You should only add it once, and then let the action do its work. 108 | 109 | Change this value if, for example, you'd like different actions maintaining different year results. However, remember to schedule your jobs such that you respect the Advent of Code request of running automated requests at a rate of no more than 1 per 15 minutes. 110 | 111 | ### `starSymbol` 112 | 113 | *Optional* - default ⭐ 114 | 115 | The symbol that will mark completed parts in your table. 116 | 117 | ### `year` 118 | 119 | *Optional* - default is year of the most recent advent 120 | 121 | Year to get results for. 122 | By default, it will get results for the year of the most recent advent. 123 | That is, this year if it's December, otherwise last year. 124 | 125 | ### `headerPrefix` 126 | 127 | *Optional* - default `##` 128 | 129 | Prefix for the section header added before the table. 130 | Should be some kind of Markdown header level. 131 | 132 | ### `readmeLocation` 133 | 134 | *Optional* - default `README.md` 135 | 136 | Location of the README file to edit. 137 | 138 | ## Like this project? 139 | 140 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/purple_img.png)](https://www.buymeacoffee.com/k2bd) 141 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: "advent-readme-stars" 2 | author: k2bd 3 | description: > 4 | Automatically update a README with an up-to-date table of your advent of code 5 | progress. 6 | runs: 7 | using: docker 8 | image: Dockerfile 9 | inputs: 10 | userId: 11 | description: > 12 | AoC user ID. See the README for instructions on getting this. 13 | required: true 14 | sessionCookie: 15 | description: > 16 | AoC session cookie - make sure to keep this in a repo secret! See the 17 | README for instructions on getting this. 18 | required: true 19 | leaderboardId: 20 | description: > 21 | AoC leaderboard ID. See the README for instructions on getting this. 22 | required: false 23 | default: "" 24 | tableMarker: 25 | description: > 26 | Unique text marking where the stars board section should go. Should be a 27 | comment, i.e. , which is not 28 | used elsewhere in the README, and which should appear alone on its own 29 | line. 30 | required: false 31 | default: "" 32 | starSymbol: 33 | description: Symbol to use in the stars table 34 | required: false 35 | default: ⭐ 36 | year: 37 | description: > 38 | Year to post the table for. Defaults to the year of the most recent 39 | December 40 | required: false 41 | default: "" 42 | headerPrefix: 43 | description: > 44 | Prefix to use for the header before the table, e.g. "##" 45 | required: false 46 | default: "##" 47 | readmeLocation: 48 | description: Location of the README file 49 | required: false 50 | default: "README.md" 51 | branding: 52 | icon: "star" 53 | color: "purple" 54 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | test: 4 | build: . 5 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "atomicwrites" 5 | version = "1.4.1" 6 | description = "Atomic file writes." 7 | optional = false 8 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 9 | files = [ 10 | {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, 11 | ] 12 | 13 | [[package]] 14 | name = "attrs" 15 | version = "23.1.0" 16 | description = "Classes Without Boilerplate" 17 | optional = false 18 | python-versions = ">=3.7" 19 | files = [ 20 | {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, 21 | {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, 22 | ] 23 | 24 | [package.extras] 25 | cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] 26 | dev = ["attrs[docs,tests]", "pre-commit"] 27 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] 28 | tests = ["attrs[tests-no-zope]", "zope-interface"] 29 | tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] 30 | 31 | [[package]] 32 | name = "black" 33 | version = "22.12.0" 34 | description = "The uncompromising code formatter." 35 | optional = false 36 | python-versions = ">=3.7" 37 | files = [ 38 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 39 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 40 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 41 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 42 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 43 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 44 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 45 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 46 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 47 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 48 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 49 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 50 | ] 51 | 52 | [package.dependencies] 53 | click = ">=8.0.0" 54 | mypy-extensions = ">=0.4.3" 55 | pathspec = ">=0.9.0" 56 | platformdirs = ">=2" 57 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 58 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 59 | 60 | [package.extras] 61 | colorama = ["colorama (>=0.4.3)"] 62 | d = ["aiohttp (>=3.7.4)"] 63 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 64 | uvloop = ["uvloop (>=0.15.2)"] 65 | 66 | [[package]] 67 | name = "certifi" 68 | version = "2023.11.17" 69 | description = "Python package for providing Mozilla's CA Bundle." 70 | optional = false 71 | python-versions = ">=3.6" 72 | files = [ 73 | {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, 74 | {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, 75 | ] 76 | 77 | [[package]] 78 | name = "charset-normalizer" 79 | version = "3.3.2" 80 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 81 | optional = false 82 | python-versions = ">=3.7.0" 83 | files = [ 84 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 85 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 86 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 87 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 88 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 89 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 90 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 91 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 92 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 93 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 94 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 95 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 96 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 97 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 98 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 99 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 100 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 101 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 102 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 103 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 104 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 105 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 106 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 107 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 108 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 109 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 110 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 111 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 112 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 113 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 114 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 115 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 116 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 117 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 118 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 119 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 120 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 121 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 122 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 123 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 124 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 125 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 126 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 127 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 128 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 129 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 130 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 131 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 132 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 133 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 134 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 135 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 136 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 137 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 138 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 139 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 140 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 141 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 142 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 143 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 144 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 145 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 146 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 147 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 148 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 149 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 150 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 151 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 152 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 153 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 154 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 155 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 156 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 157 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 158 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 159 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 160 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 161 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 162 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 163 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 164 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 165 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 166 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 167 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 168 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 169 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 170 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 171 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 172 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 173 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 174 | ] 175 | 176 | [[package]] 177 | name = "click" 178 | version = "8.1.7" 179 | description = "Composable command line interface toolkit" 180 | optional = false 181 | python-versions = ">=3.7" 182 | files = [ 183 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 184 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 185 | ] 186 | 187 | [package.dependencies] 188 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 189 | 190 | [[package]] 191 | name = "colorama" 192 | version = "0.4.6" 193 | description = "Cross-platform colored terminal text." 194 | optional = false 195 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 196 | files = [ 197 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 198 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 199 | ] 200 | 201 | [[package]] 202 | name = "coverage" 203 | version = "7.3.2" 204 | description = "Code coverage measurement for Python" 205 | optional = false 206 | python-versions = ">=3.8" 207 | files = [ 208 | {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, 209 | {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, 210 | {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, 211 | {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, 212 | {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, 213 | {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, 214 | {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, 215 | {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, 216 | {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, 217 | {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, 218 | {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, 219 | {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, 220 | {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, 221 | {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, 222 | {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, 223 | {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, 224 | {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, 225 | {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, 226 | {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, 227 | {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, 228 | {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, 229 | {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, 230 | {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, 231 | {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, 232 | {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, 233 | {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, 234 | {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, 235 | {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, 236 | {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, 237 | {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, 238 | {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, 239 | {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, 240 | {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, 241 | {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, 242 | {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, 243 | {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, 244 | {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, 245 | {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, 246 | {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, 247 | {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, 248 | {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, 249 | {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, 250 | {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, 251 | {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, 252 | {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, 253 | {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, 254 | {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, 255 | {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, 256 | {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, 257 | {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, 258 | {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, 259 | {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, 260 | ] 261 | 262 | [package.dependencies] 263 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 264 | 265 | [package.extras] 266 | toml = ["tomli"] 267 | 268 | [[package]] 269 | name = "flake8" 270 | version = "4.0.1" 271 | description = "the modular source code checker: pep8 pyflakes and co" 272 | optional = false 273 | python-versions = ">=3.6" 274 | files = [ 275 | {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, 276 | {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, 277 | ] 278 | 279 | [package.dependencies] 280 | mccabe = ">=0.6.0,<0.7.0" 281 | pycodestyle = ">=2.8.0,<2.9.0" 282 | pyflakes = ">=2.4.0,<2.5.0" 283 | 284 | [[package]] 285 | name = "idna" 286 | version = "3.6" 287 | description = "Internationalized Domain Names in Applications (IDNA)" 288 | optional = false 289 | python-versions = ">=3.5" 290 | files = [ 291 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 292 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 293 | ] 294 | 295 | [[package]] 296 | name = "iniconfig" 297 | version = "2.0.0" 298 | description = "brain-dead simple config-ini parsing" 299 | optional = false 300 | python-versions = ">=3.7" 301 | files = [ 302 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 303 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 304 | ] 305 | 306 | [[package]] 307 | name = "isort" 308 | version = "5.12.0" 309 | description = "A Python utility / library to sort Python imports." 310 | optional = false 311 | python-versions = ">=3.8.0" 312 | files = [ 313 | {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, 314 | {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, 315 | ] 316 | 317 | [package.extras] 318 | colors = ["colorama (>=0.4.3)"] 319 | pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] 320 | plugins = ["setuptools"] 321 | requirements-deprecated-finder = ["pip-api", "pipreqs"] 322 | 323 | [[package]] 324 | name = "mccabe" 325 | version = "0.6.1" 326 | description = "McCabe checker, plugin for flake8" 327 | optional = false 328 | python-versions = "*" 329 | files = [ 330 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 331 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 332 | ] 333 | 334 | [[package]] 335 | name = "mypy" 336 | version = "0.910" 337 | description = "Optional static typing for Python" 338 | optional = false 339 | python-versions = ">=3.5" 340 | files = [ 341 | {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, 342 | {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, 343 | {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, 344 | {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, 345 | {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, 346 | {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, 347 | {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, 348 | {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, 349 | {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, 350 | {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, 351 | {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, 352 | {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, 353 | {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, 354 | {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, 355 | {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, 356 | {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, 357 | {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, 358 | {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, 359 | {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, 360 | {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, 361 | {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, 362 | {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, 363 | {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, 364 | ] 365 | 366 | [package.dependencies] 367 | mypy-extensions = ">=0.4.3,<0.5.0" 368 | toml = "*" 369 | typing-extensions = ">=3.7.4" 370 | 371 | [package.extras] 372 | dmypy = ["psutil (>=4.0)"] 373 | python2 = ["typed-ast (>=1.4.0,<1.5.0)"] 374 | 375 | [[package]] 376 | name = "mypy-extensions" 377 | version = "0.4.4" 378 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 379 | optional = false 380 | python-versions = ">=2.7" 381 | files = [ 382 | {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, 383 | ] 384 | 385 | [[package]] 386 | name = "packaging" 387 | version = "23.2" 388 | description = "Core utilities for Python packages" 389 | optional = false 390 | python-versions = ">=3.7" 391 | files = [ 392 | {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, 393 | {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, 394 | ] 395 | 396 | [[package]] 397 | name = "pastel" 398 | version = "0.2.1" 399 | description = "Bring colors to your terminal." 400 | optional = false 401 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 402 | files = [ 403 | {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, 404 | {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, 405 | ] 406 | 407 | [[package]] 408 | name = "pathspec" 409 | version = "0.11.2" 410 | description = "Utility library for gitignore style pattern matching of file paths." 411 | optional = false 412 | python-versions = ">=3.7" 413 | files = [ 414 | {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, 415 | {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, 416 | ] 417 | 418 | [[package]] 419 | name = "platformdirs" 420 | version = "2.6.2" 421 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 422 | optional = false 423 | python-versions = ">=3.7" 424 | files = [ 425 | {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, 426 | {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, 427 | ] 428 | 429 | [package.extras] 430 | docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] 431 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] 432 | 433 | [[package]] 434 | name = "pluggy" 435 | version = "1.3.0" 436 | description = "plugin and hook calling mechanisms for python" 437 | optional = false 438 | python-versions = ">=3.8" 439 | files = [ 440 | {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, 441 | {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, 442 | ] 443 | 444 | [package.extras] 445 | dev = ["pre-commit", "tox"] 446 | testing = ["pytest", "pytest-benchmark"] 447 | 448 | [[package]] 449 | name = "poethepoet" 450 | version = "0.11.0" 451 | description = "A task runner that works well with poetry." 452 | optional = false 453 | python-versions = ">=3.6.2,<4.0.0" 454 | files = [ 455 | {file = "poethepoet-0.11.0-py3-none-any.whl", hash = "sha256:4aac200f52e0faa309a655c064f2a522d9d18b64f717de9805151421166ba4d8"}, 456 | {file = "poethepoet-0.11.0.tar.gz", hash = "sha256:a0618e198600db801588bae627982682f8bd5304a49a75c6dbc430feade5a8a9"}, 457 | ] 458 | 459 | [package.dependencies] 460 | pastel = ">=0.2.1,<0.3.0" 461 | tomli = ">=1.2.2,<2.0.0" 462 | 463 | [[package]] 464 | name = "py" 465 | version = "1.11.0" 466 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 467 | optional = false 468 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 469 | files = [ 470 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 471 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 472 | ] 473 | 474 | [[package]] 475 | name = "pycodestyle" 476 | version = "2.8.0" 477 | description = "Python style guide checker" 478 | optional = false 479 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 480 | files = [ 481 | {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, 482 | {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, 483 | ] 484 | 485 | [[package]] 486 | name = "pyflakes" 487 | version = "2.4.0" 488 | description = "passive checker of Python programs" 489 | optional = false 490 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 491 | files = [ 492 | {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, 493 | {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, 494 | ] 495 | 496 | [[package]] 497 | name = "pytest" 498 | version = "6.2.5" 499 | description = "pytest: simple powerful testing with Python" 500 | optional = false 501 | python-versions = ">=3.6" 502 | files = [ 503 | {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, 504 | {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, 505 | ] 506 | 507 | [package.dependencies] 508 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 509 | attrs = ">=19.2.0" 510 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 511 | iniconfig = "*" 512 | packaging = "*" 513 | pluggy = ">=0.12,<2.0" 514 | py = ">=1.8.2" 515 | toml = "*" 516 | 517 | [package.extras] 518 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 519 | 520 | [[package]] 521 | name = "pytest-cov" 522 | version = "3.0.0" 523 | description = "Pytest plugin for measuring coverage." 524 | optional = false 525 | python-versions = ">=3.6" 526 | files = [ 527 | {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, 528 | {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, 529 | ] 530 | 531 | [package.dependencies] 532 | coverage = {version = ">=5.2.1", extras = ["toml"]} 533 | pytest = ">=4.6" 534 | 535 | [package.extras] 536 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] 537 | 538 | [[package]] 539 | name = "requests" 540 | version = "2.31.0" 541 | description = "Python HTTP for Humans." 542 | optional = false 543 | python-versions = ">=3.7" 544 | files = [ 545 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 546 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 547 | ] 548 | 549 | [package.dependencies] 550 | certifi = ">=2017.4.17" 551 | charset-normalizer = ">=2,<4" 552 | idna = ">=2.5,<4" 553 | urllib3 = ">=1.21.1,<3" 554 | 555 | [package.extras] 556 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 557 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 558 | 559 | [[package]] 560 | name = "responses" 561 | version = "0.16.0" 562 | description = "A utility library for mocking out the `requests` Python library." 563 | optional = false 564 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 565 | files = [ 566 | {file = "responses-0.16.0-py2.py3-none-any.whl", hash = "sha256:f358ef75e8bf431b0aa203cc62625c3a1c80a600dbe9de91b944bf4e9c600b92"}, 567 | {file = "responses-0.16.0.tar.gz", hash = "sha256:a2e3aca2a8277e61257cd3b1c154b1dd0d782b1ae3d38b7fa37cbe3feb531791"}, 568 | ] 569 | 570 | [package.dependencies] 571 | requests = ">=2.0" 572 | six = "*" 573 | urllib3 = ">=1.25.10" 574 | 575 | [package.extras] 576 | tests = ["coverage (>=3.7.1,<6.0.0)", "flake8", "mypy", "pytest (>=4.6)", "pytest (>=4.6,<5.0)", "pytest-cov", "pytest-localserver", "types-mock", "types-requests", "types-six"] 577 | 578 | [[package]] 579 | name = "six" 580 | version = "1.16.0" 581 | description = "Python 2 and 3 compatibility utilities" 582 | optional = false 583 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 584 | files = [ 585 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 586 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 587 | ] 588 | 589 | [[package]] 590 | name = "toml" 591 | version = "0.10.2" 592 | description = "Python Library for Tom's Obvious, Minimal Language" 593 | optional = false 594 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 595 | files = [ 596 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 597 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 598 | ] 599 | 600 | [[package]] 601 | name = "tomli" 602 | version = "1.2.3" 603 | description = "A lil' TOML parser" 604 | optional = false 605 | python-versions = ">=3.6" 606 | files = [ 607 | {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, 608 | {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, 609 | ] 610 | 611 | [[package]] 612 | name = "types-requests" 613 | version = "2.31.0.10" 614 | description = "Typing stubs for requests" 615 | optional = false 616 | python-versions = ">=3.7" 617 | files = [ 618 | {file = "types-requests-2.31.0.10.tar.gz", hash = "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92"}, 619 | {file = "types_requests-2.31.0.10-py3-none-any.whl", hash = "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc"}, 620 | ] 621 | 622 | [package.dependencies] 623 | urllib3 = ">=2" 624 | 625 | [[package]] 626 | name = "typing-extensions" 627 | version = "4.8.0" 628 | description = "Backported and Experimental Type Hints for Python 3.8+" 629 | optional = false 630 | python-versions = ">=3.8" 631 | files = [ 632 | {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, 633 | {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, 634 | ] 635 | 636 | [[package]] 637 | name = "urllib3" 638 | version = "2.1.0" 639 | description = "HTTP library with thread-safe connection pooling, file post, and more." 640 | optional = false 641 | python-versions = ">=3.8" 642 | files = [ 643 | {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, 644 | {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, 645 | ] 646 | 647 | [package.extras] 648 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 649 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 650 | zstd = ["zstandard (>=0.18.0)"] 651 | 652 | [metadata] 653 | lock-version = "2.0" 654 | python-versions = "^3.8" 655 | content-hash = "12107e71c3ff5e40217247d59c3715b514f5328346de9e1a22e67f1d4e68cef7" 656 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "advent-readme-stars" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Kevin Duff "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.8" 9 | requests = "^2.26.0" 10 | # Pins 11 | platformdirs = "^2.4.0" 12 | 13 | [tool.poetry.dev-dependencies] 14 | pytest = "^6.2.5" 15 | pytest-cov = "^3.0.0" 16 | black = "^22.3.0" 17 | isort = "^5.10.1" 18 | flake8 = "^4.0.1" 19 | mypy = "^0.910" 20 | poethepoet = "^0.11.0" 21 | responses = "^0.16.0" 22 | types-requests = "^2.26.1" 23 | 24 | [tool.isort] 25 | profile = "black" 26 | multi_line_output = 3 27 | 28 | [tool.poe.tasks] 29 | autoformat.sequence = [ 30 | {cmd = "black src tests"}, 31 | {cmd = "isort src tests"}, 32 | ] 33 | 34 | lint.sequence = [ 35 | {cmd = "black --check -v src tests"}, 36 | {cmd = "isort --check -v src tests"}, 37 | {cmd = "flake8 src tests"}, 38 | {cmd = "mypy --pretty src tests"}, 39 | ] 40 | 41 | [tool.poe.tasks.test] 42 | cmd = "pytest -vv --cov=advent_readme_stars --cov-report term --cov-report xml" 43 | 44 | [tool.poe.tasks.test.env] 45 | INPUT_USERID = "1511815" 46 | INPUT_LEADERBOARDID = "9995999" 47 | INPUT_SESSIONCOOKIE = "998877665544332211" 48 | INPUT_TABLEMARKER = "" 49 | INPUT_STARSYMBOL = "⭐" 50 | INPUT_YEAR = "2019" 51 | INPUT_HEADERPREFIX = "##" 52 | ADVENT_URL = "http://fake.site.k2bd.dev" 53 | 54 | [build-system] 55 | requires = ["poetry-core>=1.0.0"] 56 | build-backend = "poetry.core.masonry.api" 57 | -------------------------------------------------------------------------------- /src/advent_readme_stars/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k2bd/advent-readme-stars/554c8de7506f0626cc06d84852a5a824e8e1f981/src/advent_readme_stars/__init__.py -------------------------------------------------------------------------------- /src/advent_readme_stars/__main__.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from advent_readme_stars.constants import README_LOCATION 3 | from advent_readme_stars.update import update_readme 4 | 5 | with open(README_LOCATION, "r") as f: 6 | lines = f.read().splitlines() 7 | 8 | edited = update_readme(lines) 9 | 10 | with open(README_LOCATION, "w") as f: 11 | f.writelines([line + "\n" for line in edited]) 12 | -------------------------------------------------------------------------------- /src/advent_readme_stars/advent.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | def most_recent_advent_year(time: datetime = None) -> int: 5 | """ 6 | Get the year of the most recent advent 7 | """ 8 | time = time or datetime.now() 9 | 10 | if time.month < 12: 11 | return time.year - 1 12 | return time.year 13 | -------------------------------------------------------------------------------- /src/advent_readme_stars/constants.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from advent_readme_stars.advent import most_recent_advent_year 4 | 5 | #: Advent of Code user ID 6 | USER_ID = os.environ.get("INPUT_USERID", "") 7 | 8 | #: Advent of Code session cookie 9 | SESSION_COOKIE = os.environ.get("INPUT_SESSIONCOOKIE", "") 10 | 11 | #: Advent of Code private leaderboard ID 12 | LEADERBOARD_ID = os.environ.get("INPUT_LEADERBOARDID", "") or USER_ID 13 | 14 | #: Marker in the README to find 15 | TABLE_MARKER = os.environ.get("INPUT_TABLEMARKER", "") 16 | 17 | #: Star symbol to insert in the table 18 | STAR_SYMBOL = os.environ.get("INPUT_STARSYMBOL", "") 19 | 20 | #: Year to query for 21 | YEAR = int(os.environ.get("INPUT_YEAR") or most_recent_advent_year()) 22 | 23 | #: Table header prefix 24 | HEADER_PREFIX = os.environ.get("INPUT_HEADERPREFIX", "") 25 | 26 | #: Location of the README file 27 | README_LOCATION = os.environ.get("INPUT_READMELOCATION", "") 28 | 29 | #: Advent of Code base URL, for testing 30 | ADVENT_URL = os.environ.get("ADVENT_URL", "https://adventofcode.com") 31 | 32 | #: Stars info endpoint 33 | STARS_ENDPOINT = f"{ADVENT_URL}/{YEAR}/leaderboard/private/view/{LEADERBOARD_ID}.json" 34 | -------------------------------------------------------------------------------- /src/advent_readme_stars/progress.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Generator 3 | 4 | import requests 5 | 6 | from advent_readme_stars.constants import SESSION_COOKIE, STARS_ENDPOINT, USER_ID 7 | 8 | 9 | @dataclass(frozen=True, eq=True) 10 | class DayProgress: 11 | day: int 12 | part_1: bool 13 | part_2: bool 14 | 15 | 16 | def get_progress() -> Generator[DayProgress, None, None]: 17 | res = requests.get(STARS_ENDPOINT, cookies={"session": SESSION_COOKIE}) 18 | res.raise_for_status() 19 | 20 | leaderboard_info = res.json() 21 | 22 | stars = leaderboard_info["members"][USER_ID]["completion_day_level"] 23 | 24 | for day, parts in stars.items(): 25 | completed = parts.keys() 26 | yield DayProgress( 27 | day=int(day), 28 | part_1="1" in completed, 29 | part_2="2" in completed, 30 | ) 31 | -------------------------------------------------------------------------------- /src/advent_readme_stars/update.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from advent_readme_stars.constants import ( 4 | ADVENT_URL, 5 | HEADER_PREFIX, 6 | STAR_SYMBOL, 7 | TABLE_MARKER, 8 | YEAR, 9 | ) 10 | from advent_readme_stars.progress import get_progress 11 | 12 | 13 | def remove_existing_table(lines: List[str]) -> List[str]: 14 | """ 15 | If there's an existing table, it should be between two table markers. 16 | If that's the case, remove the existing table and return a single table 17 | marker in its place. If not, just return the original content. 18 | """ 19 | start = None 20 | end = None 21 | for i, line in enumerate(lines): 22 | if start is None and line.strip() == TABLE_MARKER: 23 | start = i 24 | continue 25 | if start is not None and line.strip() == TABLE_MARKER: 26 | end = i 27 | break 28 | 29 | if start is not None and end is not None: 30 | return lines[:start] + lines[end:] 31 | return lines 32 | 33 | 34 | def insert_table(lines: List[str]) -> List[str]: 35 | """ 36 | Search the lines for a table marker, and insert a table there. 37 | """ 38 | table_location = None 39 | for i, line in enumerate(lines): 40 | if line.strip() == TABLE_MARKER: 41 | table_location = i 42 | break 43 | else: 44 | return lines 45 | 46 | to_insert = [ 47 | TABLE_MARKER, 48 | f"{HEADER_PREFIX} {YEAR} Results", 49 | "", 50 | "| Day | Part 1 | Part 2 |", 51 | "| :---: | :---: | :---: |", 52 | ] 53 | stars_info = sorted(list(get_progress()), key=lambda p: p.day) 54 | 55 | for star_info in stars_info: 56 | day_url = f"{ADVENT_URL}/{YEAR}/day/{star_info.day}" 57 | day_text = f"[Day {star_info.day}]({day_url})" 58 | part_1_text = STAR_SYMBOL if star_info.part_1 else " " 59 | part_2_text = STAR_SYMBOL if star_info.part_2 else " " 60 | to_insert.append(f"| {day_text} | {part_1_text} | {part_2_text} |") 61 | 62 | return lines[:table_location] + to_insert + lines[table_location:] 63 | 64 | 65 | def update_readme(readme: List[str]) -> List[str]: 66 | """ 67 | Take the contents of a readme file and update them 68 | """ 69 | reduced = remove_existing_table(readme) 70 | new_readme = insert_table(reduced) 71 | 72 | return new_readme 73 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k2bd/advent-readme-stars/554c8de7506f0626cc06d84852a5a824e8e1f981/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from .helpers import EXAMPLE_DATA_3, mock_responses 4 | 5 | 6 | @pytest.fixture 7 | def default_stars_response(): 8 | with mock_responses(EXAMPLE_DATA_3, require_all=False) as r: 9 | yield r 10 | -------------------------------------------------------------------------------- /tests/data/example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": "2021", 3 | "owner_id": "1511815", 4 | "members": { 5 | "1511815": { 6 | "id": "1511815", 7 | "global_score": 0, 8 | "local_score": 8, 9 | "stars": 2, 10 | "completion_day_level": { 11 | "1": { 12 | "1": { 13 | "get_star_ts": 1638343487 14 | }, 15 | "2": { 16 | "get_star_ts": 1638343777 17 | } 18 | } 19 | }, 20 | "last_star_ts": 1638343777, 21 | "name": "k2bd" 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/data/example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": "2020", 3 | "members": { 4 | "1511815": { 5 | "global_score": 0, 6 | "local_score": 30, 7 | "last_star_ts": 1638384651, 8 | "completion_day_level": { 9 | "6": { 10 | "2": { 11 | "get_star_ts": 1638384651 12 | }, 13 | "1": { 14 | "get_star_ts": 1638383720 15 | } 16 | }, 17 | "1": { 18 | "1": { 19 | "get_star_ts": 1637835805 20 | }, 21 | "2": { 22 | "get_star_ts": 1637836072 23 | } 24 | }, 25 | "2": { 26 | "2": { 27 | "get_star_ts": 1637842569 28 | }, 29 | "1": { 30 | "get_star_ts": 1637842194 31 | } 32 | }, 33 | "4": { 34 | "1": { 35 | "get_star_ts": 1638307291 36 | }, 37 | "2": { 38 | "get_star_ts": 1638309072 39 | } 40 | }, 41 | "3": { 42 | "2": { 43 | "get_star_ts": 1637867306 44 | }, 45 | "1": { 46 | "get_star_ts": 1637867140 47 | } 48 | }, 49 | "5": { 50 | "1": { 51 | "get_star_ts": 1638311199 52 | }, 53 | "2": { 54 | "get_star_ts": 1638311534 55 | } 56 | } 57 | }, 58 | "stars": 12, 59 | "id": "1511815", 60 | "name": "k2bd" 61 | } 62 | }, 63 | "owner_id": "1511815" 64 | } -------------------------------------------------------------------------------- /tests/data/example3.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": "2020", 3 | "members": { 4 | "1511815": { 5 | "global_score": 0, 6 | "local_score": 30, 7 | "last_star_ts": 1638384651, 8 | "completion_day_level": { 9 | "6": { 10 | "2": { 11 | "get_star_ts": 1638384651 12 | }, 13 | "1": { 14 | "get_star_ts": 1638383720 15 | } 16 | }, 17 | "1": { 18 | "1": { 19 | "get_star_ts": 1637835805 20 | }, 21 | "2": { 22 | "get_star_ts": 1637836072 23 | } 24 | }, 25 | "2": { 26 | "1": { 27 | "get_star_ts": 1637842194 28 | } 29 | }, 30 | "4": { 31 | "1": { 32 | "get_star_ts": 1638307291 33 | } 34 | }, 35 | "3": { 36 | "2": { 37 | "get_star_ts": 1637867306 38 | }, 39 | "1": { 40 | "get_star_ts": 1637867140 41 | } 42 | }, 43 | "5": { 44 | "1": { 45 | "get_star_ts": 1638311199 46 | }, 47 | "2": { 48 | "get_star_ts": 1638311534 49 | } 50 | } 51 | }, 52 | "stars": 12, 53 | "id": "1511815", 54 | "name": "k2bd" 55 | }, 56 | "1511816": { 57 | "global_score": 0, 58 | "local_score": 30, 59 | "last_star_ts": 1638384651, 60 | "completion_day_level": { 61 | "1": { 62 | "1": { 63 | "get_star_ts": 1637835805 64 | }, 65 | "2": { 66 | "get_star_ts": 1637836072 67 | } 68 | }, 69 | "2": { 70 | "1": { 71 | "get_star_ts": 1637842194 72 | } 73 | }, 74 | "3": { 75 | "2": { 76 | "get_star_ts": 1637867306 77 | }, 78 | "1": { 79 | "get_star_ts": 1637867140 80 | } 81 | } 82 | }, 83 | "stars": 12, 84 | "id": "1511816", 85 | "name": "someoneElse" 86 | } 87 | }, 88 | "owner_id": "1511815" 89 | } -------------------------------------------------------------------------------- /tests/helpers.py: -------------------------------------------------------------------------------- 1 | import os 2 | from contextlib import contextmanager 3 | from typing import Generator 4 | 5 | import responses 6 | from responses import RequestsMock 7 | 8 | from advent_readme_stars.constants import STARS_ENDPOINT 9 | 10 | _DATA_DIR = os.path.join(os.path.dirname(__file__), "data") 11 | EXAMPLE_DATA_1 = os.path.join(_DATA_DIR, "example1.json") 12 | EXAMPLE_DATA_2 = os.path.join(_DATA_DIR, "example2.json") 13 | EXAMPLE_DATA_3 = os.path.join(_DATA_DIR, "example3.json") 14 | 15 | 16 | @contextmanager 17 | def mock_responses( 18 | leaderboard_file: str, 19 | require_all: bool = False, 20 | ) -> Generator[RequestsMock, None, None]: 21 | with open(leaderboard_file) as f: 22 | body = f.read() 23 | 24 | with RequestsMock(assert_all_requests_are_fired=require_all) as r: 25 | r.add(responses.GET, STARS_ENDPOINT, body=body) 26 | 27 | yield r 28 | -------------------------------------------------------------------------------- /tests/test_advent.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | import pytest 4 | 5 | from advent_readme_stars.advent import most_recent_advent_year 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "now, expected", 10 | [ 11 | (datetime(1999, 11, 30), 1998), 12 | (datetime(1999, 12, 1), 1999), 13 | (datetime(1999, 12, 31), 1999), 14 | (datetime(2000, 1, 1), 1999), 15 | ], 16 | ) 17 | def test_most_recent_advent_year(now, expected): 18 | assert most_recent_advent_year(now) == expected 19 | -------------------------------------------------------------------------------- /tests/test_progress.py: -------------------------------------------------------------------------------- 1 | from advent_readme_stars.progress import DayProgress, get_progress 2 | 3 | from .helpers import EXAMPLE_DATA_1, EXAMPLE_DATA_2, EXAMPLE_DATA_3, mock_responses 4 | 5 | 6 | def test_get_progress_example1(): 7 | with mock_responses(EXAMPLE_DATA_1): 8 | stars = set(get_progress()) 9 | 10 | assert stars == { 11 | DayProgress(day=1, part_1=True, part_2=True), 12 | } 13 | 14 | 15 | def test_get_progress_example2(): 16 | with mock_responses(EXAMPLE_DATA_2): 17 | stars = set(get_progress()) 18 | 19 | assert stars == { 20 | DayProgress(day=1, part_1=True, part_2=True), 21 | DayProgress(day=2, part_1=True, part_2=True), 22 | DayProgress(day=3, part_1=True, part_2=True), 23 | DayProgress(day=4, part_1=True, part_2=True), 24 | DayProgress(day=5, part_1=True, part_2=True), 25 | DayProgress(day=6, part_1=True, part_2=True), 26 | } 27 | 28 | 29 | def test_get_progress_example3(): 30 | with mock_responses(EXAMPLE_DATA_3): 31 | stars = set(get_progress()) 32 | 33 | assert stars == { 34 | DayProgress(day=1, part_1=True, part_2=True), 35 | DayProgress(day=2, part_1=True, part_2=False), 36 | DayProgress(day=3, part_1=True, part_2=True), 37 | DayProgress(day=4, part_1=True, part_2=False), 38 | DayProgress(day=5, part_1=True, part_2=True), 39 | DayProgress(day=6, part_1=True, part_2=True), 40 | } 41 | -------------------------------------------------------------------------------- /tests/test_update.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from advent_readme_stars.constants import TABLE_MARKER 4 | from advent_readme_stars.update import ( 5 | insert_table, 6 | remove_existing_table, 7 | update_readme, 8 | ) 9 | 10 | DEFAULT_TABLE_CONTENT = [ 11 | "## 2019 Results", 12 | "", 13 | "| Day | Part 1 | Part 2 |", 14 | "| :---: | :---: | :---: |", 15 | "| [Day 1](http://fake.site.k2bd.dev/2019/day/1) | ⭐ | ⭐ |", 16 | "| [Day 2](http://fake.site.k2bd.dev/2019/day/2) | ⭐ | |", 17 | "| [Day 3](http://fake.site.k2bd.dev/2019/day/3) | ⭐ | ⭐ |", 18 | "| [Day 4](http://fake.site.k2bd.dev/2019/day/4) | ⭐ | |", 19 | "| [Day 5](http://fake.site.k2bd.dev/2019/day/5) | ⭐ | ⭐ |", 20 | "| [Day 6](http://fake.site.k2bd.dev/2019/day/6) | ⭐ | ⭐ |", 21 | ] 22 | 23 | 24 | @pytest.mark.parametrize( 25 | "lines, expected", 26 | [ 27 | ( 28 | [ 29 | "AAAAA", 30 | TABLE_MARKER, 31 | "BBBBB", 32 | "CCCCC", 33 | "DDDDD", 34 | TABLE_MARKER, 35 | "EEEEE", 36 | ], 37 | [ 38 | "AAAAA", 39 | TABLE_MARKER, 40 | "EEEEE", 41 | ], 42 | ), 43 | ( 44 | [ 45 | TABLE_MARKER, 46 | TABLE_MARKER, 47 | "AAAAA", 48 | "BBBBB", 49 | "CCCCC", 50 | "DDDDD", 51 | "EEEEE", 52 | ], 53 | [ 54 | TABLE_MARKER, 55 | "AAAAA", 56 | "BBBBB", 57 | "CCCCC", 58 | "DDDDD", 59 | "EEEEE", 60 | ], 61 | ), 62 | ( 63 | [ 64 | "AAAAA", 65 | "BBBBB", 66 | "CCCCC", 67 | "DDDDD", 68 | "EEEEE", 69 | TABLE_MARKER, 70 | TABLE_MARKER, 71 | ], 72 | [ 73 | "AAAAA", 74 | "BBBBB", 75 | "CCCCC", 76 | "DDDDD", 77 | "EEEEE", 78 | TABLE_MARKER, 79 | ], 80 | ), 81 | ( 82 | [ 83 | TABLE_MARKER, 84 | "AAAAA", 85 | "BBBBB", 86 | "CCCCC", 87 | "DDDDD", 88 | "EEEEE", 89 | TABLE_MARKER, 90 | ], 91 | [ 92 | TABLE_MARKER, 93 | ], 94 | ), 95 | ( 96 | [ 97 | TABLE_MARKER, 98 | "AAAAA", 99 | "BBBBB", 100 | "CCCCC", 101 | "DDDDD", 102 | "EEEEE", 103 | ], 104 | [ 105 | TABLE_MARKER, 106 | "AAAAA", 107 | "BBBBB", 108 | "CCCCC", 109 | "DDDDD", 110 | "EEEEE", 111 | ], 112 | ), 113 | ( 114 | [ 115 | "AAAAA", 116 | "BBBBB", 117 | "CCCCC", 118 | TABLE_MARKER, 119 | "DDDDD", 120 | "EEEEE", 121 | ], 122 | [ 123 | "AAAAA", 124 | "BBBBB", 125 | "CCCCC", 126 | TABLE_MARKER, 127 | "DDDDD", 128 | "EEEEE", 129 | ], 130 | ), 131 | ( 132 | [ 133 | "AAAAA", 134 | "BBBBB", 135 | "CCCCC", 136 | "DDDDD", 137 | "EEEEE", 138 | TABLE_MARKER, 139 | ], 140 | [ 141 | "AAAAA", 142 | "BBBBB", 143 | "CCCCC", 144 | "DDDDD", 145 | "EEEEE", 146 | TABLE_MARKER, 147 | ], 148 | ), 149 | ( 150 | [ 151 | "AAAAA", 152 | "BBBBB", 153 | "CCCCC", 154 | "DDDDD", 155 | "EEEEE", 156 | ], 157 | [ 158 | "AAAAA", 159 | "BBBBB", 160 | "CCCCC", 161 | "DDDDD", 162 | "EEEEE", 163 | ], 164 | ), 165 | ], 166 | ) 167 | def test_remove_existing_table(lines, expected): 168 | assert remove_existing_table(lines) == expected 169 | 170 | 171 | @pytest.mark.parametrize( 172 | "lines, expected", 173 | [ 174 | ( 175 | [ 176 | TABLE_MARKER, 177 | "AAAAA", 178 | "BBBBB", 179 | "CCCCC", 180 | "DDDDD", 181 | "EEEEE", 182 | ], 183 | [ 184 | TABLE_MARKER, 185 | *DEFAULT_TABLE_CONTENT, 186 | TABLE_MARKER, 187 | "AAAAA", 188 | "BBBBB", 189 | "CCCCC", 190 | "DDDDD", 191 | "EEEEE", 192 | ], 193 | ), 194 | ( 195 | [ 196 | "AAAAA", 197 | "BBBBB", 198 | "CCCCC", 199 | TABLE_MARKER, 200 | "DDDDD", 201 | "EEEEE", 202 | ], 203 | [ 204 | "AAAAA", 205 | "BBBBB", 206 | "CCCCC", 207 | TABLE_MARKER, 208 | *DEFAULT_TABLE_CONTENT, 209 | TABLE_MARKER, 210 | "DDDDD", 211 | "EEEEE", 212 | ], 213 | ), 214 | ( 215 | [ 216 | "AAAAA", 217 | "BBBBB", 218 | "CCCCC", 219 | "DDDDD", 220 | "EEEEE", 221 | TABLE_MARKER, 222 | ], 223 | [ 224 | "AAAAA", 225 | "BBBBB", 226 | "CCCCC", 227 | "DDDDD", 228 | "EEEEE", 229 | TABLE_MARKER, 230 | *DEFAULT_TABLE_CONTENT, 231 | TABLE_MARKER, 232 | ], 233 | ), 234 | ( 235 | [ 236 | "AAAAA", 237 | "BBBBB", 238 | "CCCCC", 239 | "DDDDD", 240 | "EEEEE", 241 | ], 242 | [ 243 | "AAAAA", 244 | "BBBBB", 245 | "CCCCC", 246 | "DDDDD", 247 | "EEEEE", 248 | ], 249 | ), 250 | ], 251 | ) 252 | def test_insert_table(lines, expected, default_stars_response): 253 | assert insert_table(lines) == expected 254 | 255 | 256 | @pytest.mark.parametrize( 257 | "lines, expected", 258 | [ 259 | ( 260 | [ 261 | "AAAAA", 262 | "BBBBB", 263 | "CCCCC", 264 | TABLE_MARKER, 265 | "FFFFF", 266 | "GGGGG", 267 | "HHHHH", 268 | TABLE_MARKER, 269 | "DDDDD", 270 | "EEEEE", 271 | ], 272 | [ 273 | "AAAAA", 274 | "BBBBB", 275 | "CCCCC", 276 | TABLE_MARKER, 277 | *DEFAULT_TABLE_CONTENT, 278 | TABLE_MARKER, 279 | "DDDDD", 280 | "EEEEE", 281 | ], 282 | ), 283 | ( 284 | [ 285 | "AAAAA", 286 | "BBBBB", 287 | "CCCCC", 288 | "DDDDD", 289 | "EEEEE", 290 | ], 291 | [ 292 | "AAAAA", 293 | "BBBBB", 294 | "CCCCC", 295 | "DDDDD", 296 | "EEEEE", 297 | ], 298 | ), 299 | ], 300 | ) 301 | def test_update_readme(lines, expected, default_stars_response): 302 | assert update_readme(lines) == expected 303 | --------------------------------------------------------------------------------