├── _config.yml ├── static └── demo.gif ├── config.ini ├── Pipfile ├── index.html ├── templates ├── 401.html └── newspie.html ├── requirements.txt ├── LICENSE ├── data └── countries.json ├── README.md ├── .gitignore ├── news.py └── Pipfile.lock /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | -------------------------------------------------------------------------------- /static/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skamieniarz/newspie/HEAD/static/demo.gif -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [ENDPOINTS] 2 | TOP_HEADLINES = https://newsapi.org/v2/top-headlines 3 | EVERYTHING = https://newsapi.org/v2/everything 4 | 5 | [VARIOUS] 6 | TEMPLATE = newspie.html 7 | 401_TEMPLATE = 401.html 8 | PAGE_SIZE = 6 -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | pylint = "*" 8 | yapf = "*" 9 | flake8 = "*" 10 | 11 | [packages] 12 | requests = "*" 13 | flask = "*" 14 | requests-cache = "*" -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Redirecting to https://newspie.eu.pythonanywhere.com/ 4 | 5 | 6 | -------------------------------------------------------------------------------- /templates/401.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | NewsPie 9 | 10 | 11 |
12 |

API key is invalid or its configuration is incorrect (not saved as an environment variable or as a string in the code).

13 |
14 | 15 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://pypi.org/simple 2 | attrs==23.1.0; python_version >= '3.7' 3 | blinker==1.6.2; python_version >= '3.7' 4 | cattrs==23.1.2; python_version >= '3.7' 5 | certifi==2023.7.22; python_version >= '3.6' 6 | charset-normalizer==3.2.0; python_full_version >= '3.7.0' 7 | click==8.1.7; python_version >= '3.7' 8 | flask==2.3.3 9 | idna==3.4; python_version >= '3.5' 10 | itsdangerous==2.1.2; python_version >= '3.7' 11 | jinja2==3.1.2; python_version >= '3.7' 12 | markupsafe==2.1.3; python_version >= '3.7' 13 | platformdirs==3.10.0; python_version >= '3.7' 14 | requests==2.31.0 15 | requests-cache==1.1.0 16 | six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' 17 | url-normalize==1.4.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' 18 | urllib3==2.0.4; python_version >= '3.7' 19 | werkzeug==2.3.7; python_version >= '3.8' 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 skamieniarz 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 | -------------------------------------------------------------------------------- /data/countries.json: -------------------------------------------------------------------------------- 1 | { 2 | "ar": "Argentina", 3 | "au": "Australia", 4 | "at": "Austria", 5 | "be": "Belgium", 6 | "br": "Brazil", 7 | "bg": "Bulgaria", 8 | "ca": "Canada", 9 | "cn": "China", 10 | "co": "Colombia", 11 | "cu": "Cuba", 12 | "cz": "Czechia", 13 | "eg": "Egypt", 14 | "fr": "France", 15 | "de": "Germany", 16 | "gr": "Greece", 17 | "hk": "Hong Kong", 18 | "hu": "Hungary", 19 | "in": "India", 20 | "id": "Indonesia", 21 | "ie": "Ireland", 22 | "il": "Israel", 23 | "it": "Italy", 24 | "jp": "Japan", 25 | "lv": "Latvia", 26 | "lt": "Lithuania", 27 | "my": "Malaysia", 28 | "mx": "Mexico", 29 | "ma": "Morocco", 30 | "nl": "Netherlands", 31 | "nz": "New Zealand", 32 | "ng": "Nigeria", 33 | "no": "Norway", 34 | "ph": "Philippines", 35 | "pl": "Poland", 36 | "pt": "Portugal", 37 | "kr": "South Korea", 38 | "ro": "Romania", 39 | "ru": "Russia", 40 | "sa": "Saudi Arabia", 41 | "rs": "Serbia", 42 | "sg": "Singapore", 43 | "sk": "Slovakia", 44 | "si": "Slovenia", 45 | "za": "South Africa", 46 | "se": "Sweden", 47 | "ch": "Switzerland", 48 | "tw": "Taiwan", 49 | "th": "Thailand", 50 | "tr": "Turkey", 51 | "ua": "Ukraine", 52 | "ae": "UAE", 53 | "gb": "UK", 54 | "us": "USA", 55 | "ve": "Venezuela" 56 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NewsPie # 2 | A minimalistic news aggregator built with [Flask](https://www.palletsprojects.com/p/flask/) and powered by [News API](https://newsapi.org/). 3 | 4 | ![Demo!](https://raw.githubusercontent.com/skamieniarz/newspie/master/static/demo.gif) 5 | 6 | ## Prerequisites: ## 7 | 8 | 1. [Python3](https://www.python.org). 9 | 2. Dependencies from `requirements.txt` or `Pipfile.lock` (preferably in a virtual env). 10 | 3. API key for [News API](https://newsapi.org/register). It's free for non-commercial projects (including open-source) and allows 500 requests per day. Once acquired, save it as `NEWS_API_KEY` environment variable or directly as string to `API_KEY` in `news.py`. Just don't share it or upload the code with it. 11 | 12 | ## Starting: ## 13 | 14 | 1. Run `python news.py` or `python3 news.py` (depending on the environment) in the terminal while in the root of the repository. 15 | 2. Go to [http://127.0.0.1:5000/](http://127.0.0.1:5000/) address in the web browser. 16 | 17 | ## Features: ## 18 | 19 | 1. Get top articles headlines and their URLs by country and/or category. 20 | 2. Search articles up to a month old. Looks for a given query in the titles. Language/country independent results. Relevancy decides. 21 | 3. Results received from News API are cached for 5 minutes. 22 | 4. Country saved as a cookie. 23 | 5. Responsive UI. 24 | 6. Dark mode/theme. 25 | 26 | ## Open-source used: ## 27 | 28 | - [Flask](https://github.com/pallets/flask) 29 | - [jinja](https://github.com/pallets/jinja) 30 | - [Pico.css](https://github.com/picocss/pico) 31 | - [requests](https://github.com/psf/requests) 32 | - [requests-cache](https://github.com/reclosedev/requests-cache) 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_store 3 | .vscode/ 4 | news_cache.sqlite 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | pip-wheel-metadata/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | *.py,cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | -------------------------------------------------------------------------------- /templates/newspie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | NewsPie 9 | 10 | 11 |
12 |
13 | NewsPie{% for item in categories %}{% if item == category %} • {{ item }} 14 | {% else %} • {{ item }} 15 | {% endif %}{% endfor %} 16 |
17 |
18 | 19 | 20 | {% for article in articles %} 21 | 22 | 25 | 26 | {% endfor %} 27 | 28 |
{{ article['title'] }} 24 |
29 |
30 | {% if page == 1 %} 31 | {% else %} 32 | {% endif %} 33 | 34 | {% if page >= pages %} 35 | {% else %} 36 | {% endif %} 37 |
38 |
39 |
40 | 42 | 43 |
44 |
45 |
46 | 54 |
55 |
56 | {% if theme == "light" %} 57 | {% else %} 58 | {% endif %} 59 |
60 |
61 |
62 | 68 | 69 | -------------------------------------------------------------------------------- /news.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | NewsPie - a minimalistic news aggregator built with Flask and powered by 4 | News API (https://newsapi.org/). 5 | 6 | Created by @skamieniarz (https://github.com/skamieniarz) in 2019. 7 | ''' 8 | import configparser 9 | import json 10 | import logging 11 | import os 12 | from typing import Union 13 | 14 | import requests 15 | import requests_cache 16 | from flask import (Flask, make_response, redirect, render_template, request, 17 | url_for) 18 | 19 | CONFIG = configparser.ConfigParser() 20 | CONFIG.read('config.ini') 21 | API_KEY = os.environ.get('NEWS_API_KEY') 22 | TOP_HEADLINES = CONFIG['ENDPOINTS']['TOP_HEADLINES'] 23 | EVERYTHING = CONFIG['ENDPOINTS']['EVERYTHING'] 24 | PAGE_SIZE = int(CONFIG['VARIOUS']['PAGE_SIZE']) 25 | 26 | CATEGORIES = ('general', 'sports', 'business', 'entertainment', 'health', 27 | 'science', 'technology') 28 | with open('data/countries.json') as json_file: 29 | COUNTRIES = json.load(json_file) 30 | 31 | logging.basicConfig(level=logging.DEBUG) 32 | requests_cache.install_cache(cache_name='news_cache', 33 | backend='sqlite', 34 | expire_after=300) 35 | 36 | APP = Flask(__name__) 37 | SESSION = requests.Session() 38 | SESSION.headers.update({'Authorization': API_KEY}) 39 | 40 | 41 | @APP.route('/', methods=['GET', 'POST']) 42 | def root(): 43 | ''' Base URL redirect to the first page of general category. ''' 44 | return redirect(url_for('category', category='general', page=1)) 45 | 46 | 47 | @APP.errorhandler(404) 48 | def page_not_found(error): 49 | ''' Not existing pages redirect to the first page of general category. ''' 50 | return redirect(url_for('category', category='general', page=1)) 51 | 52 | 53 | @APP.route('/category/', methods=['GET', 'POST']) 54 | def category(category): 55 | ''' Handles category route. 56 | 57 | Parameters: 58 | - name: category 59 | in: path 60 | description: Name of the news category 61 | - name: page 62 | in: query 63 | description: Number of the page 64 | ''' 65 | page = request.args.get('page', default=1, type=int) 66 | if page < 1: 67 | return redirect(url_for('category', category=category, page=1)) 68 | if request.method == 'POST' and category in CATEGORIES: 69 | return do_post(page, category) 70 | if category in CATEGORIES: 71 | params = {'page': page, 'category': category, 'pageSize': PAGE_SIZE} 72 | country = get_cookie('country') 73 | if country is not None: 74 | params.update({'country': country}) 75 | response = SESSION.get(TOP_HEADLINES, params=params) 76 | if response.status_code == 200: 77 | pages = count_pages(response.json()) 78 | if page > pages: 79 | page = pages 80 | return redirect( 81 | url_for('category', category=category, page=page)) 82 | articles = parse_articles(response.json()) 83 | theme = get_cookie('theme') if get_cookie( 84 | 'theme') is not None else 'light' 85 | return render(articles, page, pages, country, category, theme) 86 | elif response.status_code == 401: 87 | return render_template(CONFIG['VARIOUS']['401_TEMPLATE']) 88 | return redirect(url_for('category', category='general', page=page)) 89 | 90 | 91 | @APP.route('/search/', methods=['GET', 'POST']) 92 | def search(query: str): 93 | ''' Handles category route. 94 | 95 | Parameters: 96 | - name: query 97 | in: path 98 | description: Query string to be searched 99 | - name: page 100 | in: query 101 | description: Number of the page 102 | ''' 103 | page = request.args.get('page', default=1, type=int) 104 | if page < 1: 105 | return redirect(url_for('search', query=query, page=1)) 106 | params = { 107 | 'qInTitle': query, 108 | 'sortBy': 'relevancy', 109 | 'page': page, 110 | 'pageSize': PAGE_SIZE 111 | } 112 | if request.method == 'POST': 113 | return do_post(page, category='search', current_query=query) 114 | response = SESSION.get(EVERYTHING, params=params) 115 | pages = count_pages(response.json()) 116 | if page > pages: 117 | page = pages 118 | return redirect(url_for('search', query=query, page=page)) 119 | articles = parse_articles(response.json()) 120 | return render(articles, 121 | page, 122 | pages, 123 | country=get_cookie('country'), 124 | category='search', 125 | theme=get_cookie('theme')) 126 | 127 | 128 | def do_post(page=1, category='general', current_query=None): 129 | ''' Helper method that handles POST request basing on the input. ''' 130 | new_query = request.form.get('search_query') 131 | country = request.form.get('country') 132 | theme = request.form.get('theme') 133 | next_page = request.form.get('next_page') 134 | previous_page = request.form.get('previous_page') 135 | if new_query is not None and new_query != '': 136 | return redirect(url_for('search', query=new_query, page=1)) 137 | if country is not None and country != get_cookie('country'): 138 | response = make_response( 139 | redirect(url_for('category', category=category, page=1))) 140 | response.set_cookie('country', country) 141 | return response 142 | if theme is not None: 143 | response = make_response( 144 | redirect(url_for('category', category=category, page=page))) 145 | response.set_cookie('theme', theme) 146 | return response 147 | if next_page is not None: 148 | page = int(next_page) + 1 149 | elif previous_page is not None: 150 | page = int(previous_page) - 1 151 | if category == 'search': 152 | return redirect(url_for('search', query=current_query, page=page)) 153 | return redirect(url_for('category', category=category, page=page)) 154 | 155 | 156 | def parse_articles(response: dict) -> list: 157 | ''' Parses articles fetched from News API. 158 | 159 | Returns: 160 | A list of dicts containing publishing title and URL. 161 | ''' 162 | parsed_articles = [] 163 | if response.get('status') == 'ok': 164 | for article in response.get('articles'): 165 | parsed_articles.append({ 166 | 'title': article['title'], 167 | 'url': article['url'] 168 | }) 169 | return parsed_articles 170 | 171 | 172 | def count_pages(response: dict) -> int: 173 | ''' Helper method that counts number of total pages basing on total 174 | results from News API response and PAGE_SIZE. 175 | 176 | Returns: 177 | An int with a number of total pages. ''' 178 | if response.get('status') == 'ok': 179 | return (-(-response.get('totalResults', 0) // PAGE_SIZE)) 180 | return 0 181 | 182 | 183 | def render(articles: list, page: int, pages: int, country: str, category: str, 184 | theme: str): 185 | ''' Renders the template with appropriate variables. Up to 12 pages 186 | allowed. ''' 187 | pages = pages if pages <= 12 else 12 188 | return render_template(CONFIG['VARIOUS']['TEMPLATE'], 189 | articles=articles, 190 | categories=CATEGORIES, 191 | category=category, 192 | countries=COUNTRIES, 193 | country=country, 194 | page=page, 195 | pages=pages, 196 | theme=theme) 197 | 198 | 199 | def get_cookie(key: str) -> Union[str, None]: 200 | ''' Helper method that gets cookie's value. 201 | 202 | Returns: 203 | A string with a value of a cookie with provided key. If a key is 204 | missing, None is returned. 205 | ''' 206 | return request.cookies.get(key) 207 | 208 | 209 | if __name__ == '__main__': 210 | APP.run() 211 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "0e27b578cbaccfd58327d1bff71a9aea8519c51e35fc267c023355e93bb3fda9" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "attrs": { 18 | "hashes": [ 19 | "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", 20 | "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" 21 | ], 22 | "markers": "python_version >= '3.7'", 23 | "version": "==23.1.0" 24 | }, 25 | "blinker": { 26 | "hashes": [ 27 | "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", 28 | "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" 29 | ], 30 | "markers": "python_version >= '3.7'", 31 | "version": "==1.6.2" 32 | }, 33 | "cattrs": { 34 | "hashes": [ 35 | "sha256:b2bb14311ac17bed0d58785e5a60f022e5431aca3932e3fc5cc8ed8639de50a4", 36 | "sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657" 37 | ], 38 | "markers": "python_version >= '3.7'", 39 | "version": "==23.1.2" 40 | }, 41 | "certifi": { 42 | "hashes": [ 43 | "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", 44 | "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" 45 | ], 46 | "markers": "python_version >= '3.6'", 47 | "version": "==2023.7.22" 48 | }, 49 | "charset-normalizer": { 50 | "hashes": [ 51 | "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", 52 | "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", 53 | "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", 54 | "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", 55 | "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", 56 | "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", 57 | "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", 58 | "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", 59 | "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", 60 | "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", 61 | "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", 62 | "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", 63 | "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", 64 | "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", 65 | "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", 66 | "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", 67 | "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", 68 | "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", 69 | "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", 70 | "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", 71 | "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", 72 | "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", 73 | "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", 74 | "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", 75 | "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", 76 | "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", 77 | "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", 78 | "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", 79 | "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", 80 | "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", 81 | "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", 82 | "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", 83 | "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", 84 | "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", 85 | "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", 86 | "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", 87 | "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", 88 | "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", 89 | "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", 90 | "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", 91 | "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", 92 | "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", 93 | "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", 94 | "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", 95 | "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", 96 | "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", 97 | "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", 98 | "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", 99 | "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", 100 | "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", 101 | "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", 102 | "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", 103 | "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", 104 | "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", 105 | "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", 106 | "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", 107 | "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", 108 | "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", 109 | "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", 110 | "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", 111 | "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", 112 | "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", 113 | "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", 114 | "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", 115 | "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", 116 | "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", 117 | "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", 118 | "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", 119 | "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", 120 | "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", 121 | "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", 122 | "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", 123 | "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", 124 | "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", 125 | "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" 126 | ], 127 | "markers": "python_full_version >= '3.7.0'", 128 | "version": "==3.2.0" 129 | }, 130 | "click": { 131 | "hashes": [ 132 | "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", 133 | "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" 134 | ], 135 | "markers": "python_version >= '3.7'", 136 | "version": "==8.1.7" 137 | }, 138 | "flask": { 139 | "hashes": [ 140 | "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc", 141 | "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" 142 | ], 143 | "index": "pypi", 144 | "version": "==2.3.3" 145 | }, 146 | "idna": { 147 | "hashes": [ 148 | "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", 149 | "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" 150 | ], 151 | "markers": "python_version >= '3.5'", 152 | "version": "==3.4" 153 | }, 154 | "itsdangerous": { 155 | "hashes": [ 156 | "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", 157 | "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" 158 | ], 159 | "markers": "python_version >= '3.7'", 160 | "version": "==2.1.2" 161 | }, 162 | "jinja2": { 163 | "hashes": [ 164 | "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", 165 | "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" 166 | ], 167 | "markers": "python_version >= '3.7'", 168 | "version": "==3.1.2" 169 | }, 170 | "markupsafe": { 171 | "hashes": [ 172 | "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", 173 | "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", 174 | "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", 175 | "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", 176 | "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", 177 | "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", 178 | "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", 179 | "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", 180 | "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", 181 | "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", 182 | "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", 183 | "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", 184 | "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", 185 | "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", 186 | "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", 187 | "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", 188 | "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", 189 | "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", 190 | "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", 191 | "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", 192 | "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", 193 | "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", 194 | "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", 195 | "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", 196 | "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", 197 | "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", 198 | "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", 199 | "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", 200 | "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", 201 | "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", 202 | "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", 203 | "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", 204 | "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", 205 | "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", 206 | "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", 207 | "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", 208 | "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", 209 | "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", 210 | "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", 211 | "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", 212 | "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", 213 | "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", 214 | "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", 215 | "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", 216 | "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", 217 | "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", 218 | "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", 219 | "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", 220 | "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", 221 | "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" 222 | ], 223 | "markers": "python_version >= '3.7'", 224 | "version": "==2.1.3" 225 | }, 226 | "platformdirs": { 227 | "hashes": [ 228 | "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d", 229 | "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d" 230 | ], 231 | "markers": "python_version >= '3.7'", 232 | "version": "==3.10.0" 233 | }, 234 | "requests": { 235 | "hashes": [ 236 | "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", 237 | "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" 238 | ], 239 | "index": "pypi", 240 | "version": "==2.31.0" 241 | }, 242 | "requests-cache": { 243 | "hashes": [ 244 | "sha256:178282bce704b912c59e7f88f367c42bddd6cde6bf511b2a3e3cfb7e5332a92a", 245 | "sha256:41b79166aa8e300cc4de982f7ab7c52af914a785160be1eda25c6e9265969a67" 246 | ], 247 | "index": "pypi", 248 | "version": "==1.1.0" 249 | }, 250 | "six": { 251 | "hashes": [ 252 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 253 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 254 | ], 255 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", 256 | "version": "==1.16.0" 257 | }, 258 | "url-normalize": { 259 | "hashes": [ 260 | "sha256:d23d3a070ac52a67b83a1c59a0e68f8608d1cd538783b401bc9de2c0fac999b2", 261 | "sha256:ec3c301f04e5bb676d333a7fa162fa977ad2ca04b7e652bfc9fac4e405728eed" 262 | ], 263 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 264 | "version": "==1.4.3" 265 | }, 266 | "urllib3": { 267 | "hashes": [ 268 | "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11", 269 | "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4" 270 | ], 271 | "markers": "python_version >= '3.7'", 272 | "version": "==2.0.4" 273 | }, 274 | "werkzeug": { 275 | "hashes": [ 276 | "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8", 277 | "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528" 278 | ], 279 | "markers": "python_version >= '3.8'", 280 | "version": "==2.3.7" 281 | } 282 | }, 283 | "develop": { 284 | "astroid": { 285 | "hashes": [ 286 | "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c", 287 | "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd" 288 | ], 289 | "markers": "python_full_version >= '3.7.2'", 290 | "version": "==2.15.6" 291 | }, 292 | "dill": { 293 | "hashes": [ 294 | "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", 295 | "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" 296 | ], 297 | "markers": "python_version >= '3.11'", 298 | "version": "==0.3.7" 299 | }, 300 | "flake8": { 301 | "hashes": [ 302 | "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23", 303 | "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5" 304 | ], 305 | "index": "pypi", 306 | "version": "==6.1.0" 307 | }, 308 | "importlib-metadata": { 309 | "hashes": [ 310 | "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb", 311 | "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743" 312 | ], 313 | "markers": "python_version >= '3.8'", 314 | "version": "==6.8.0" 315 | }, 316 | "isort": { 317 | "hashes": [ 318 | "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", 319 | "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" 320 | ], 321 | "markers": "python_full_version >= '3.8.0'", 322 | "version": "==5.12.0" 323 | }, 324 | "lazy-object-proxy": { 325 | "hashes": [ 326 | "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", 327 | "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", 328 | "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", 329 | "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", 330 | "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", 331 | "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", 332 | "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", 333 | "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", 334 | "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", 335 | "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", 336 | "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", 337 | "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", 338 | "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", 339 | "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", 340 | "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", 341 | "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", 342 | "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", 343 | "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", 344 | "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", 345 | "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", 346 | "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", 347 | "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", 348 | "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", 349 | "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", 350 | "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", 351 | "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", 352 | "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", 353 | "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", 354 | "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", 355 | "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", 356 | "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", 357 | "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", 358 | "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", 359 | "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", 360 | "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", 361 | "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59" 362 | ], 363 | "markers": "python_version >= '3.7'", 364 | "version": "==1.9.0" 365 | }, 366 | "mccabe": { 367 | "hashes": [ 368 | "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", 369 | "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" 370 | ], 371 | "markers": "python_version >= '3.6'", 372 | "version": "==0.7.0" 373 | }, 374 | "platformdirs": { 375 | "hashes": [ 376 | "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d", 377 | "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d" 378 | ], 379 | "markers": "python_version >= '3.7'", 380 | "version": "==3.10.0" 381 | }, 382 | "pycodestyle": { 383 | "hashes": [ 384 | "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0", 385 | "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8" 386 | ], 387 | "markers": "python_version >= '3.8'", 388 | "version": "==2.11.0" 389 | }, 390 | "pyflakes": { 391 | "hashes": [ 392 | "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774", 393 | "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc" 394 | ], 395 | "markers": "python_version >= '3.8'", 396 | "version": "==3.1.0" 397 | }, 398 | "pylint": { 399 | "hashes": [ 400 | "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413", 401 | "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252" 402 | ], 403 | "index": "pypi", 404 | "version": "==2.17.5" 405 | }, 406 | "tomli": { 407 | "hashes": [ 408 | "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", 409 | "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" 410 | ], 411 | "markers": "python_version >= '3.7'", 412 | "version": "==2.0.1" 413 | }, 414 | "tomlkit": { 415 | "hashes": [ 416 | "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", 417 | "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899" 418 | ], 419 | "markers": "python_version >= '3.7'", 420 | "version": "==0.12.1" 421 | }, 422 | "wrapt": { 423 | "hashes": [ 424 | "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", 425 | "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", 426 | "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", 427 | "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", 428 | "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", 429 | "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", 430 | "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", 431 | "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", 432 | "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", 433 | "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", 434 | "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", 435 | "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", 436 | "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", 437 | "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", 438 | "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", 439 | "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", 440 | "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", 441 | "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", 442 | "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", 443 | "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", 444 | "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", 445 | "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", 446 | "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", 447 | "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", 448 | "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", 449 | "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", 450 | "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", 451 | "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", 452 | "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", 453 | "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", 454 | "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", 455 | "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", 456 | "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", 457 | "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", 458 | "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", 459 | "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", 460 | "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", 461 | "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", 462 | "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", 463 | "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", 464 | "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", 465 | "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", 466 | "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", 467 | "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", 468 | "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", 469 | "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", 470 | "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", 471 | "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", 472 | "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", 473 | "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", 474 | "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", 475 | "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", 476 | "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", 477 | "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", 478 | "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", 479 | "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", 480 | "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", 481 | "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", 482 | "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", 483 | "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", 484 | "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", 485 | "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", 486 | "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", 487 | "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", 488 | "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", 489 | "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", 490 | "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", 491 | "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", 492 | "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", 493 | "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", 494 | "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", 495 | "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", 496 | "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", 497 | "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", 498 | "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" 499 | ], 500 | "markers": "python_version >= '3.11'", 501 | "version": "==1.15.0" 502 | }, 503 | "yapf": { 504 | "hashes": [ 505 | "sha256:958587eb5c8ec6c860119a9c25d02addf30a44f75aa152a4220d30e56a98037c", 506 | "sha256:b8bfc1f280949153e795181768ca14ef43d7312629a06c43e7abd279323af313" 507 | ], 508 | "index": "pypi", 509 | "version": "==0.40.1" 510 | }, 511 | "zipp": { 512 | "hashes": [ 513 | "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0", 514 | "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147" 515 | ], 516 | "markers": "python_version >= '3.8'", 517 | "version": "==3.16.2" 518 | } 519 | } 520 | } 521 | --------------------------------------------------------------------------------