diff --git a/backend/.gitattributes b/.gitattributes similarity index 100% rename from backend/.gitattributes rename to .gitattributes diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..fb3bccbf --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +backend/.vercel +backend/.env +backend/node_modules +backend/*.lock +backend/coverage +backend/benchmarks +backend/vercel_token +frontend/frontend/.env + +# IDE +.idea/ +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json +*.code-workspace diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index b1d9a017..00000000 --- a/backend/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -.vercel -.env -node_modules -*.lock -.idea/ -coverage -benchmarks -vercel_token - -# IDE -.vscode/* -!.vscode/extensions.json -!.vscode/settings.json -*.code-workspace - -.vercel diff --git a/backend/vercel.json b/backend/vercel.json index 6df00e01..1b206ef7 100644 --- a/backend/vercel.json +++ b/backend/vercel.json @@ -11,6 +11,5 @@ "source": "/frontend/:match*", "destination": "https://monorepo-test-frontend-neon.vercel.app/:match*" } - ], - "relatedProjects": ["prj_emQEOHSrnlodZCgZ1kyzVsiiGSQm"] + ] } \ No newline at end of file diff --git a/frontend/.gitattributes b/frontend/.gitattributes deleted file mode 100644 index dfe07704..00000000 --- a/frontend/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/frontend/.github/workflows/backend.yaml b/frontend/.github/workflows/backend.yaml deleted file mode 100644 index 6c6160cd..00000000 --- a/frontend/.github/workflows/backend.yaml +++ /dev/null @@ -1,39 +0,0 @@ -name: CI-Backend - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.11 - uses: actions/setup-python@v2 - with: - python-version: 3.11 - - name: Install dependencies - run: | - cd backend - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Test with unittest - run: | - cd backend - python -m unittest - env: - AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }} - MONGODB_PASSWORD: ${{ secrets.MONGODB_PASSWORD }} - - name: Upload coverage to Coveralls - run: | - cd backend - coverage run --source=src -m unittest - coveralls - env: - AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }} - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} - MONGODB_PASSWORD: ${{ secrets.MONGODB_PASSWORD }} diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index 0b4ea1b1..00000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -*.pyc -__pycache__ - -.vscode - -backend/.env -backend/.venv -backend/.coverage -backend/gcloud_key.json - -frontend/.env -.DS_Store diff --git a/frontend/backend/.coveragerc b/frontend/backend/.coveragerc deleted file mode 100644 index 51eca306..00000000 --- a/frontend/backend/.coveragerc +++ /dev/null @@ -1,15 +0,0 @@ -[run] -source = src - -omit = - ./.venv/* - ./tests/* - ./models/* - */__init__.py - -[report] -omit = - ./.venv/* - ./tests/* - ./models/* - */__init__.py \ No newline at end of file diff --git a/frontend/backend/.env-template b/frontend/backend/.env-template deleted file mode 100644 index 38281c92..00000000 --- a/frontend/backend/.env-template +++ /dev/null @@ -1,14 +0,0 @@ -AUTH_TOKEN=abc123 -COVERALLS_REPO_TOKEN=abc123 - -PROD_OAUTH_CLIENT_ID=abc123 -PROD_OAUTH_CLIENT_SECRET=abc123 -PROD_OAUTH_REDIRECT_URI=abc123 - -DEV_OAUTH_CLIENT_ID=abc123 -DEV_OAUTH_CLIENT_SECRET=abc123 -DEV_OAUTH_REDIRECT_URI=abc123 - -GOOGLE_APPLICATION_CREDENTIALS=abc123 - -MONGODB_PASSWORD=abc123 diff --git a/frontend/backend/.flake8 b/frontend/backend/.flake8 deleted file mode 100644 index b685fd68..00000000 --- a/frontend/backend/.flake8 +++ /dev/null @@ -1,5 +0,0 @@ -[flake8] -max-line-length = 88 -max-complexity = 100 -select = B,C,E,F,W,T -ignore = E203, W503, E501 \ No newline at end of file diff --git a/frontend/backend/.gcloudignore b/frontend/backend/.gcloudignore deleted file mode 100644 index 7eab7ca8..00000000 --- a/frontend/backend/.gcloudignore +++ /dev/null @@ -1,34 +0,0 @@ -# This file specifies files that are *not* uploaded to Google Cloud Platform -# using gcloud. It follows the same syntax as .gitignore, with the addition of -# "#!include" directives (which insert the entries of the given .gitignore-style -# file at that point). -# -# For more information, run: -# $ gcloud topic gcloudignore -# -.gcloudignore -# If you would like to upload your .git directory, .gitignore file or files -# from your .gitignore file, remove the corresponding line -# below: -.git -.gitignore - -# Python pycache: -__pycache__/ -.venv -.coverage -.coveragerc -.flake8 -poetry.lock -pyproject.toml -README.md - -# Ignored by the build system -/setup.cfg - -# remove irrelevant files/folders -deploy -tests -.env-template -.pre-commit-config.yaml -gcloud_key.json \ No newline at end of file diff --git a/frontend/backend/.pre-commit-config.yaml b/frontend/backend/.pre-commit-config.yaml deleted file mode 100644 index 74a849ac..00000000 --- a/frontend/backend/.pre-commit-config.yaml +++ /dev/null @@ -1,11 +0,0 @@ -repos: - - repo: https://github.com/ambv/black - rev: 21.10b0 - hooks: - - id: black - language_version: python3.11 - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 - hooks: - - id: flake8 - args: [--config=./backend/.flake8] diff --git a/frontend/backend/README.md b/frontend/backend/README.md deleted file mode 100644 index 532cf057..00000000 --- a/frontend/backend/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Backend - -## Installation - -``` -poetry install -poetry run pre-commit install -``` - -## Run Locally - -Navigate to localhost:8000 - -``` -yarn start -``` - -## Test with Code Coverage - -``` -yarn test -``` - -View coverage with GitHub badge or on coveralls.io - -## Build - -If a new requirement has been added, make sure to update the requirements.txt - -``` -yarn set-reqs -``` - -Then, just commit on the main branch (Google Cloud Run takes care of the rest) - -## Adding a Secret - -Update cloudbuild.yaml, .env, .env-template, and GCP Cloud Run Trigger Substitution Variables. diff --git a/frontend/backend/deploy/app.yaml b/frontend/backend/deploy/app.yaml deleted file mode 100644 index fef26e47..00000000 --- a/frontend/backend/deploy/app.yaml +++ /dev/null @@ -1,14 +0,0 @@ -service: default -runtime: python311 -entrypoint: gunicorn -w 2 -k uvicorn.workers.UvicornWorker src.main:app - -#smallest instance class -instance_class: F1 - -#prevents creating additional instances -automatic_scaling: - min_instances: 0 - max_instances: 1 - -env_variables: - PROD: true diff --git a/frontend/backend/deploy/cloudbuild.yaml b/frontend/backend/deploy/cloudbuild.yaml deleted file mode 100644 index 15e244cf..00000000 --- a/frontend/backend/deploy/cloudbuild.yaml +++ /dev/null @@ -1,21 +0,0 @@ -steps: - - name: node:10.15.1 - entrypoint: npm - args: ["install"] - dir: "backend" - - name: node:10.15.1 - entrypoint: npm - args: ["run", "create-env"] - dir: "backend" - env: - - "DEV_OAUTH_CLIENT_ID=${_DEV_OAUTH_CLIENT_ID}" - - "DEV_OAUTH_CLIENT_SECRET=${_DEV_OAUTH_CLIENT_SECRET}" - - "DEV_OAUTH_REDIRECT_URI=${_DEV_OAUTH_REDIRECT_URI}" - - "PROD_OAUTH_CLIENT_ID=${_PROD_OAUTH_CLIENT_ID}" - - "PROD_OAUTH_CLIENT_SECRET=${_PROD_OAUTH_CLIENT_SECRET}" - - "PROD_OAUTH_REDIRECT_URI=${_PROD_OAUTH_REDIRECT_URI}" - - "MONGODB_PASSWORD=${_MONGODB_PASSWORD}" - - "SENTRY_DSN=${_SENTRY_DSN}" - - name: "gcr.io/cloud-builders/gcloud" - args: ["app", "deploy", "--appyaml", "./deploy/app.yaml"] - dir: "backend" diff --git a/frontend/backend/deploy/dispatch.yaml b/frontend/backend/deploy/dispatch.yaml deleted file mode 100644 index c4dc2328..00000000 --- a/frontend/backend/deploy/dispatch.yaml +++ /dev/null @@ -1,3 +0,0 @@ -dispatch: - - url: "*/.*" - service: default diff --git a/frontend/backend/package.json b/frontend/backend/package.json deleted file mode 100644 index b7d72d71..00000000 --- a/frontend/backend/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "github-trends", - "version": "0.0.1", - "private": true, - "scripts": { - "gen-lang-map": "poetry run python src/data/github/language_map.py", - "start": "poetry run uvicorn src.main:app --reload --port=8000", - "set-reqs": "poetry lock && poetry export -f requirements.txt --output requirements.txt --without-hashes", - "create-env": "printenv > .env", - "test": "poetry run coverage run --source=src -m unittest -v && poetry run coverage report", - "isort": "poetry run isort . --src-path=./src --multi-line=3 --trailing-comma --line-length=88 --combine-as --ensure-newline-before-comments" - } -} diff --git a/frontend/backend/poetry.lock b/frontend/backend/poetry.lock deleted file mode 100644 index 46c60268..00000000 --- a/frontend/backend/poetry.lock +++ /dev/null @@ -1,1673 +0,0 @@ -# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. - -[[package]] -name = "aiofiles" -version = "23.2.1" -description = "File support for asyncio." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, - {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, -] - -[[package]] -name = "aiounittest" -version = "1.4.2" -description = "Test asyncio code more easily." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "aiounittest-1.4.2-py3-none-any.whl", hash = "sha256:11634918fbbf09b954d97c74d857835750d025d1cafb675391d0099b8d83665a"}, - {file = "aiounittest-1.4.2.tar.gz", hash = "sha256:1478ec8013c70046569cdc76d541083ca7da362e6513cf29c7ecb51330229bdb"}, -] - -[package.dependencies] -wrapt = "*" - -[[package]] -name = "annotated-types" -version = "0.6.0" -description = "Reusable constraint types to use with typing.Annotated" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, -] - -[[package]] -name = "anyio" -version = "3.7.1" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] - -[[package]] -name = "black" -version = "23.11.0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, - {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, - {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, - {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, - {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, - {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, - {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, - {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, - {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, - {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, - {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, - {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, - {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, - {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, - {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, - {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, - {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, - {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2023.11.17" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "6.5.0" -description = "Code coverage measurement for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, - {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, - {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, - {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, - {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, - {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, - {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, - {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, - {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, - {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, - {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, - {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, - {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, - {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, - {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, - {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, -] - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "coveralls" -version = "3.3.1" -description = "Show coverage stats online via coveralls.io" -category = "main" -optional = false -python-versions = ">= 3.5" -files = [ - {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, - {file = "coveralls-3.3.1.tar.gz", hash = "sha256:b32a8bb5d2df585207c119d6c01567b81fba690c9c10a753bfe27a335bfc43ea"}, -] - -[package.dependencies] -coverage = ">=4.1,<6.0.0 || >6.1,<6.1.1 || >6.1.1,<7.0" -docopt = ">=0.6.1" -requests = ">=1.0.0" - -[package.extras] -yaml = ["PyYAML (>=3.10)"] - -[[package]] -name = "distlib" -version = "0.3.7" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, -] - -[[package]] -name = "dnspython" -version = "2.4.2" -description = "DNS toolkit" -category = "main" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, - {file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, -] - -[package.extras] -dnssec = ["cryptography (>=2.6,<42.0)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.24.1)"] -doq = ["aioquic (>=0.9.20)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.23)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] - -[[package]] -name = "docopt" -version = "0.6.2" -description = "Pythonic argument parser, that will make you smile" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, -] - -[[package]] -name = "fastapi" -version = "0.104.1" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fastapi-0.104.1-py3-none-any.whl", hash = "sha256:752dc31160cdbd0436bb93bad51560b57e525cbb1d4bbf6f4904ceee75548241"}, - {file = "fastapi-0.104.1.tar.gz", hash = "sha256:e5e4540a7c5e1dcfbbcf5b903c234feddcdcd881f191977a1c5dfd917487e7ae"}, -] - -[package.dependencies] -anyio = ">=3.7.1,<4.0.0" -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.27.0,<0.28.0" -typing-extensions = ">=4.8.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "filelock" -version = "3.13.1" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] - -[[package]] -name = "flake8" -version = "6.1.0" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" - -[[package]] -name = "grpcio" -version = "1.59.3" -description = "HTTP/2-based RPC framework" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpcio-1.59.3-cp310-cp310-linux_armv7l.whl", hash = "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc"}, - {file = "grpcio-1.59.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354"}, - {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c"}, - {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4"}, - {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d"}, - {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25"}, - {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8"}, - {file = "grpcio-1.59.3-cp310-cp310-win32.whl", hash = "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab"}, - {file = "grpcio-1.59.3-cp310-cp310-win_amd64.whl", hash = "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a"}, - {file = "grpcio-1.59.3-cp311-cp311-linux_armv7l.whl", hash = "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86"}, - {file = "grpcio-1.59.3-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3"}, - {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf"}, - {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d"}, - {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118"}, - {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea"}, - {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473"}, - {file = "grpcio-1.59.3-cp311-cp311-win32.whl", hash = "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3"}, - {file = "grpcio-1.59.3-cp311-cp311-win_amd64.whl", hash = "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9"}, - {file = "grpcio-1.59.3-cp312-cp312-linux_armv7l.whl", hash = "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7"}, - {file = "grpcio-1.59.3-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b"}, - {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025"}, - {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d"}, - {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d"}, - {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9"}, - {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718"}, - {file = "grpcio-1.59.3-cp312-cp312-win32.whl", hash = "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79"}, - {file = "grpcio-1.59.3-cp312-cp312-win_amd64.whl", hash = "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07"}, - {file = "grpcio-1.59.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94"}, - {file = "grpcio-1.59.3-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53"}, - {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a"}, - {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069"}, - {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e"}, - {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b"}, - {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb"}, - {file = "grpcio-1.59.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e"}, - {file = "grpcio-1.59.3-cp38-cp38-linux_armv7l.whl", hash = "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556"}, - {file = "grpcio-1.59.3-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e"}, - {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8"}, - {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6"}, - {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3"}, - {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69"}, - {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881"}, - {file = "grpcio-1.59.3-cp38-cp38-win32.whl", hash = "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0"}, - {file = "grpcio-1.59.3-cp38-cp38-win_amd64.whl", hash = "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391"}, - {file = "grpcio-1.59.3-cp39-cp39-linux_armv7l.whl", hash = "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552"}, - {file = "grpcio-1.59.3-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59"}, - {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb"}, - {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994"}, - {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13"}, - {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d"}, - {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7"}, - {file = "grpcio-1.59.3-cp39-cp39-win32.whl", hash = "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281"}, - {file = "grpcio-1.59.3-cp39-cp39-win_amd64.whl", hash = "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e"}, - {file = "grpcio-1.59.3.tar.gz", hash = "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.59.3)"] - -[[package]] -name = "gunicorn" -version = "21.2.0" -description = "WSGI HTTP Server for UNIX" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, - {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, -] - -[package.dependencies] -packaging = "*" - -[package.extras] -eventlet = ["eventlet (>=0.24.1)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "httptools" -version = "0.6.1" -description = "A collection of framework independent HTTP protocol utils." -category = "main" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, - {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, - {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, - {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, - {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, - {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, - {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, -] - -[package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] - -[[package]] -name = "identify" -version = "2.5.32" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "identify-2.5.32-py2.py3-none-any.whl", hash = "sha256:0b7656ef6cba81664b783352c73f8c24b39cf82f926f78f4550eda928e5e0545"}, - {file = "identify-2.5.32.tar.gz", hash = "sha256:5d9979348ec1a21c768ae07e0a652924538e8bce67313a73cb0f681cf08ba407"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "motor" -version = "3.3.2" -description = "Non-blocking MongoDB driver for Tornado or asyncio" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "motor-3.3.2-py3-none-any.whl", hash = "sha256:6fe7e6f0c4f430b9e030b9d22549b732f7c2226af3ab71ecc309e4a1b7d19953"}, - {file = "motor-3.3.2.tar.gz", hash = "sha256:d2fc38de15f1c8058f389c1a44a4d4105c0405c48c061cd492a654496f7bc26a"}, -] - -[package.dependencies] -pymongo = ">=4.5,<5" - -[package.extras] -aws = ["pymongo[aws] (>=4.5,<5)"] -encryption = ["pymongo[encryption] (>=4.5,<5)"] -gssapi = ["pymongo[gssapi] (>=4.5,<5)"] -ocsp = ["pymongo[ocsp] (>=4.5,<5)"] -snappy = ["pymongo[snappy] (>=4.5,<5)"] -srv = ["pymongo[srv] (>=4.5,<5)"] -test = ["aiohttp (<3.8.6)", "mockupdb", "motor[encryption]", "pytest (>=7)", "tornado (>=5)"] -zstd = ["pymongo[zstd] (>=4.5,<5)"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pathspec" -version = "0.11.2" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, -] - -[[package]] -name = "platformdirs" -version = "4.0.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, -] - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] - -[[package]] -name = "pre-commit" -version = "3.5.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, - {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pycodestyle" -version = "2.11.1" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, -] - -[[package]] -name = "pydantic" -version = "2.5.2" -description = "Data validation using Python type hints" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, - {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, -] - -[package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.14.5" -typing-extensions = ">=4.6.1" - -[package.extras] -email = ["email-validator (>=2.0.0)"] - -[[package]] -name = "pydantic-core" -version = "2.14.5" -description = "" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, - {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, - {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, - {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, - {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, - {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, - {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, - {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, - {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, - {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, - {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, - {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, - {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, - {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, - {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, - {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.1.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, -] - -[[package]] -name = "pyinstrument" -version = "4.6.1" -description = "Call stack profiler for Python. Shows you why your code is slow!" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyinstrument-4.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:73476e4bc6e467ac1b2c3c0dd1f0b71c9061d4de14626676adfdfbb14aa342b4"}, - {file = "pyinstrument-4.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4d1da8efd974cf9df52ee03edaee2d3875105ddd00de35aa542760f7c612bdf7"}, - {file = "pyinstrument-4.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:507be1ee2f2b0c9fba74d622a272640dd6d1b0c9ec3388b2cdeb97ad1e77125f"}, - {file = "pyinstrument-4.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95cee6de08eb45754ef4f602ce52b640d1c535d934a6a8733a974daa095def37"}, - {file = "pyinstrument-4.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7873e8cec92321251fdf894a72b3c78f4c5c20afdd1fef0baf9042ec843bb04"}, - {file = "pyinstrument-4.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a242f6cac40bc83e1f3002b6b53681846dfba007f366971db0bf21e02dbb1903"}, - {file = "pyinstrument-4.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:97c9660cdb4bd2a43cf4f3ab52cffd22f3ac9a748d913b750178fb34e5e39e64"}, - {file = "pyinstrument-4.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e304cd0723e2b18ada5e63c187abf6d777949454c734f5974d64a0865859f0f4"}, - {file = "pyinstrument-4.6.1-cp310-cp310-win32.whl", hash = "sha256:cee21a2d78187dd8a80f72f5d0f1ddb767b2d9800f8bb4d94b6d11f217c22cdb"}, - {file = "pyinstrument-4.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:2000712f71d693fed2f8a1c1638d37b7919124f367b37976d07128d49f1445eb"}, - {file = "pyinstrument-4.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a366c6f3dfb11f1739bdc1dee75a01c1563ad0bf4047071e5e77598087df457f"}, - {file = "pyinstrument-4.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6be327be65d934796558aa9cb0f75ce62ebd207d49ad1854610c97b0579ad47"}, - {file = "pyinstrument-4.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e160d9c5d20d3e4ef82269e4e8b246ff09bdf37af5fb8cb8ccca97936d95ad6"}, - {file = "pyinstrument-4.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ffbf56605ef21c2fcb60de2fa74ff81f417d8be0c5002a407e414d6ef6dee43"}, - {file = "pyinstrument-4.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92cc4924596d6e8f30a16182bbe90893b1572d847ae12652f72b34a9a17c24a"}, - {file = "pyinstrument-4.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f4b48a94d938cae981f6948d9ec603bab2087b178d2095d042d5a48aabaecaab"}, - {file = "pyinstrument-4.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7a386392275bdef4a1849712dc5b74f0023483fca14ef93d0ca27d453548982"}, - {file = "pyinstrument-4.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:871b131b83e9b1122f2325061c68ed1e861eebcb568c934d2fb193652f077f77"}, - {file = "pyinstrument-4.6.1-cp311-cp311-win32.whl", hash = "sha256:8d8515156dd91f5652d13b5fcc87e634f8fe1c07b68d1d0840348cdd50bf5ace"}, - {file = "pyinstrument-4.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb868fbe089036e9f32525a249f4c78b8dc46967612393f204b8234f439c9cc4"}, - {file = "pyinstrument-4.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a18cd234cce4f230f1733807f17a134e64a1f1acabf74a14d27f583cf2b183df"}, - {file = "pyinstrument-4.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:574cfca69150be4ce4461fb224712fbc0722a49b0dc02fa204d02807adf6b5a0"}, - {file = "pyinstrument-4.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e02cf505e932eb8ccf561b7527550a67ec14fcae1fe0e25319b09c9c166e914"}, - {file = "pyinstrument-4.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832fb2acef9d53701c1ab546564c45fb70a8770c816374f8dd11420d399103c9"}, - {file = "pyinstrument-4.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13cb57e9607545623ebe462345b3d0c4caee0125d2d02267043ece8aca8f4ea0"}, - {file = "pyinstrument-4.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9be89e7419bcfe8dd6abb0d959d6d9c439c613a4a873514c43d16b48dae697c9"}, - {file = "pyinstrument-4.6.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:476785cfbc44e8e1b1ad447398aa3deae81a8df4d37eb2d8bbb0c404eff979cd"}, - {file = "pyinstrument-4.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e9cebd90128a3d2fee36d3ccb665c1b9dce75261061b2046203e45c4a8012d54"}, - {file = "pyinstrument-4.6.1-cp312-cp312-win32.whl", hash = "sha256:1d0b76683df2ad5c40eff73607dc5c13828c92fbca36aff1ddf869a3c5a55fa6"}, - {file = "pyinstrument-4.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:c4b7af1d9d6a523cfbfedebcb69202242d5bd0cb89c4e094cc73d5d6e38279bd"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79ae152f8c6a680a188fb3be5e0f360ac05db5bbf410169a6c40851dfaebcce9"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cad2745964c174c65aa75f1bf68a4394d1b4d28f33894837cfd315d1e836f0"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb81f66f7f94045d723069cf317453d42375de9ff3c69089cf6466b078ac1db4"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab30ae75969da99e9a529e21ff497c18fdf958e822753db4ae7ed1e67094040"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f36cb5b644762fb3c86289324bbef17e95f91cd710603ac19444a47f638e8e96"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8b45075d9dbbc977dbc7007fb22bb0054c6990fbe91bf48dd80c0b96c6307ba7"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:475ac31477f6302e092463896d6a2055f3e6abcd293bad16ff94fc9185308a88"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-win32.whl", hash = "sha256:29172ab3d8609fdf821c3f2562dc61e14f1a8ff5306607c32ca743582d3a760e"}, - {file = "pyinstrument-4.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:bd176f297c99035127b264369d2bb97a65255f65f8d4e843836baf55ebb3cee4"}, - {file = "pyinstrument-4.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:23e9b4526978432e9999021da9a545992cf2ac3df5ee82db7beb6908fc4c978c"}, - {file = "pyinstrument-4.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2dbcaccc9f456ef95557ec501caeb292119c24446d768cb4fb43578b0f3d572c"}, - {file = "pyinstrument-4.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2097f63c66c2bc9678c826b9ff0c25acde3ed455590d9dcac21220673fe74fbf"}, - {file = "pyinstrument-4.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:205ac2e76bd65d61b9611a9ce03d5f6393e34ec5b41dd38808f25d54e6b3e067"}, - {file = "pyinstrument-4.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f414ddf1161976a40fc0a333000e6a4ad612719eac0b8c9bb73f47153187148"}, - {file = "pyinstrument-4.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:65e62ebfa2cd8fb57eda90006f4505ac4c70da00fc2f05b6d8337d776ea76d41"}, - {file = "pyinstrument-4.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d96309df4df10be7b4885797c5f69bb3a89414680ebaec0722d8156fde5268c3"}, - {file = "pyinstrument-4.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f3d1ad3bc8ebb4db925afa706aa865c4bfb40d52509f143491ac0df2440ee5d2"}, - {file = "pyinstrument-4.6.1-cp38-cp38-win32.whl", hash = "sha256:dc37cb988c8854eb42bda2e438aaf553536566657d157c4473cc8aad5692a779"}, - {file = "pyinstrument-4.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:2cd4ce750c34a0318fc2d6c727cc255e9658d12a5cf3f2d0473f1c27157bdaeb"}, - {file = "pyinstrument-4.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ca95b21f022e995e062b371d1f42d901452bcbedd2c02f036de677119503355"}, - {file = "pyinstrument-4.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ac1e1d7e1f1b64054c4eb04eb4869a7a5eef2261440e73943cc1b1bc3c828c18"}, - {file = "pyinstrument-4.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0711845e953fce6ab781221aacffa2a66dbc3289f8343e5babd7b2ea34da6c90"}, - {file = "pyinstrument-4.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b7d28582017de35cb64eb4e4fa603e753095108ca03745f5d17295970ee631f"}, - {file = "pyinstrument-4.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7be57db08bd366a37db3aa3a6187941ee21196e8b14975db337ddc7d1490649d"}, - {file = "pyinstrument-4.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9a0ac0f56860398d2628ce389826ce83fb3a557d0c9a2351e8a2eac6eb869983"}, - {file = "pyinstrument-4.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a9045186ff13bc826fef16be53736a85029aae3c6adfe52e666cad00d7ca623b"}, - {file = "pyinstrument-4.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6c4c56b6eab9004e92ad8a48bb54913fdd71fc8a748ae42a27b9e26041646f8b"}, - {file = "pyinstrument-4.6.1-cp39-cp39-win32.whl", hash = "sha256:37e989c44b51839d0c97466fa2b623638b9470d56d79e329f359f0e8fa6d83db"}, - {file = "pyinstrument-4.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:5494c5a84fee4309d7d973366ca6b8b9f8ba1d6b254e93b7c506264ef74f2cef"}, - {file = "pyinstrument-4.6.1.tar.gz", hash = "sha256:f4731b27121350f5a983d358d2272fe3df2f538aed058f57217eef7801a89288"}, -] - -[package.extras] -bin = ["click", "nox"] -docs = ["furo (==2021.6.18b36)", "myst-parser (==0.15.1)", "sphinx (==4.2.0)", "sphinxcontrib-programoutput (==0.17)"] -examples = ["django", "numpy"] -test = ["flaky", "greenlet (>=3.0.0a1)", "ipython", "pytest", "pytest-asyncio (==0.12.0)", "sphinx-autobuild (==2021.3.14)", "trio"] -types = ["typing-extensions"] - -[[package]] -name = "pymongo" -version = "4.6.0" -description = "Python driver for MongoDB " -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pymongo-4.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c011bd5ad03cc096f99ffcfdd18a1817354132c1331bed7a837a25226659845f"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux1_i686.whl", hash = "sha256:5e63146dbdb1eac207464f6e0cfcdb640c9c5ff0f57b754fa96fe252314a1dc6"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:2972dd1f1285866aba027eff2f4a2bbf8aa98563c2ced14cb34ee5602b36afdf"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:a0be99b599da95b7a90a918dd927b20c434bea5e1c9b3efc6a3c6cd67c23f813"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:9b0f98481ad5dc4cb430a60bbb8869f05505283b9ae1c62bdb65eb5e020ee8e3"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:256c503a75bd71cf7fb9ebf889e7e222d49c6036a48aad5a619f98a0adf0e0d7"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:b4ad70d7cac4ca0c7b31444a0148bd3af01a2662fa12b1ad6f57cd4a04e21766"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5717a308a703dda2886a5796a07489c698b442f5e409cf7dc2ac93de8d61d764"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f7f9feecae53fa18d6a3ea7c75f9e9a1d4d20e5c3f9ce3fba83f07bcc4eee2"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128b1485753106c54af481789cdfea12b90a228afca0b11fb3828309a907e10e"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3077a31633beef77d057c6523f5de7271ddef7bde5e019285b00c0cc9cac1e3"}, - {file = "pymongo-4.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebf02c32afa6b67e5861a27183dd98ed88419a94a2ab843cc145fb0bafcc5b28"}, - {file = "pymongo-4.6.0-cp310-cp310-win32.whl", hash = "sha256:b14dd73f595199f4275bed4fb509277470d9b9059310537e3b3daba12b30c157"}, - {file = "pymongo-4.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:8adf014f2779992eba3b513e060d06f075f0ab2fb3ad956f413a102312f65cdf"}, - {file = "pymongo-4.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba51129fcc510824b6ca6e2ce1c27e3e4d048b6e35d3ae6f7e517bed1b8b25ce"}, - {file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2973f113e079fb98515722cd728e1820282721ec9fd52830e4b73cabdbf1eb28"}, - {file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af425f323fce1b07755edd783581e7283557296946212f5b1a934441718e7528"}, - {file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ec71ac633b126c0775ed4604ca8f56c3540f5c21a1220639f299e7a544b55f9"}, - {file = "pymongo-4.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ec6c20385c5a58e16b1ea60c5e4993ea060540671d7d12664f385f2fb32fe79"}, - {file = "pymongo-4.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:85f2cdc400ee87f5952ebf2a117488f2525a3fb2e23863a8efe3e4ee9e54e4d1"}, - {file = "pymongo-4.6.0-cp311-cp311-win32.whl", hash = "sha256:7fc2bb8a74dcfcdd32f89528e38dcbf70a3a6594963d60dc9595e3b35b66e414"}, - {file = "pymongo-4.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6695d7136a435c1305b261a9ddb9b3ecec9863e05aab3935b96038145fd3a977"}, - {file = "pymongo-4.6.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d603edea1ff7408638b2504905c032193b7dcee7af269802dbb35bc8c3310ed5"}, - {file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79f41576b3022c2fe9780ae3e44202b2438128a25284a8ddfa038f0785d87019"}, - {file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f2af6cf82509b15093ce3569229e0d53c90ad8ae2eef940652d4cf1f81e045"}, - {file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecd9e1fa97aa11bf67472220285775fa15e896da108f425e55d23d7540a712ce"}, - {file = "pymongo-4.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d2be5c9c3488fa8a70f83ed925940f488eac2837a996708d98a0e54a861f212"}, - {file = "pymongo-4.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ab6bcc8e424e07c1d4ba6df96f7fb963bcb48f590b9456de9ebd03b88084fe8"}, - {file = "pymongo-4.6.0-cp312-cp312-win32.whl", hash = "sha256:47aa128be2e66abd9d1a9b0437c62499d812d291f17b55185cb4aa33a5f710a4"}, - {file = "pymongo-4.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:014e7049dd019a6663747ca7dae328943e14f7261f7c1381045dfc26a04fa330"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:288c21ab9531b037f7efa4e467b33176bc73a0c27223c141b822ab4a0e66ff2a"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:747c84f4e690fbe6999c90ac97246c95d31460d890510e4a3fa61b7d2b87aa34"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:055f5c266e2767a88bb585d01137d9c7f778b0195d3dbf4a487ef0638be9b651"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:82e620842e12e8cb4050d2643a81c8149361cd82c0a920fa5a15dc4ca8a4000f"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:6b18276f14b4b6d92e707ab6db19b938e112bd2f1dc3f9f1a628df58e4fd3f0d"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:680fa0fc719e1a3dcb81130858368f51d83667d431924d0bcf249644bce8f303"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:3919708594b86d0f5cdc713eb6fccd3f9b9532af09ea7a5d843c933825ef56c4"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db082f728160369d9a6ed2e722438291558fc15ce06d0a7d696a8dad735c236b"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e4ed21029d80c4f62605ab16398fe1ce093fff4b5f22d114055e7d9fbc4adb0"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bea9138b0fc6e2218147e9c6ce1ff76ff8e29dc00bb1b64842bd1ca107aee9f"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a0269811661ba93c472c8a60ea82640e838c2eb148d252720a09b5123f2c2fe"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d6a1b1361f118e7fefa17ae3114e77f10ee1b228b20d50c47c9f351346180c8"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e3b0127b260d4abae7b62203c4c7ef0874c901b55155692353db19de4b18bc4"}, - {file = "pymongo-4.6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a49aca4d961823b2846b739380c847e8964ff7ae0f0a683992b9d926054f0d6d"}, - {file = "pymongo-4.6.0-cp37-cp37m-win32.whl", hash = "sha256:09c7de516b08c57647176b9fc21d929d628e35bcebc7422220c89ae40b62126a"}, - {file = "pymongo-4.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:81dd1308bd5630d2bb5980f00aa163b986b133f1e9ed66c66ce2a5bc3572e891"}, - {file = "pymongo-4.6.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:2f8c04277d879146eacda920476e93d520eff8bec6c022ac108cfa6280d84348"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5802acc012bbb4bce4dff92973dff76482f30ef35dd4cb8ab5b0e06aa8f08c80"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ccd785fafa1c931deff6a7116e9a0d402d59fabe51644b0d0c268295ff847b25"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fe03bf25fae4b95d8afe40004a321df644400fdcba4c8e5e1a19c1085b740888"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:2ca0ba501898b2ec31e6c3acf90c31910944f01d454ad8e489213a156ccf1bda"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:10a379fb60f1b2406ae57b8899bacfe20567918c8e9d2d545e1b93628fcf2050"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a4dc1319d0c162919ee7f4ee6face076becae2abbd351cc14f1fe70af5fb20d9"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ddef295aaf80cefb0c1606f1995899efcb17edc6b327eb6589e234e614b87756"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:518c90bdd6e842c446d01a766b9136fec5ec6cc94f3b8c3f8b4a332786ee6b64"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b80a4ee19b3442c57c38afa978adca546521a8822d663310b63ae2a7d7b13f3a"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb438a8bf6b695bf50d57e6a059ff09652a07968b2041178b3744ea785fcef9b"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3db7d833a7c38c317dc95b54e27f1d27012e031b45a7c24e360b53197d5f6e7"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3729b8db02063da50eeb3db88a27670d85953afb9a7f14c213ac9e3dca93034b"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:39a1cd5d383b37285641d5a7a86be85274466ae336a61b51117155936529f9b3"}, - {file = "pymongo-4.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7b0e6361754ac596cd16bfc6ed49f69ffcd9b60b7bc4bcd3ea65c6a83475e4ff"}, - {file = "pymongo-4.6.0-cp38-cp38-win32.whl", hash = "sha256:806e094e9e85d8badc978af8c95b69c556077f11844655cb8cd2d1758769e521"}, - {file = "pymongo-4.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1394c4737b325166a65ae7c145af1ebdb9fb153ebedd37cf91d676313e4a67b8"}, - {file = "pymongo-4.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a8273e1abbcff1d7d29cbbb1ea7e57d38be72f1af3c597c854168508b91516c2"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:e16ade71c93f6814d095d25cd6d28a90d63511ea396bd96e9ffcb886b278baaa"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:325701ae7b56daa5b0692305b7cb505ca50f80a1288abb32ff420a8a209b01ca"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:cc94f9fea17a5af8cf1a343597711a26b0117c0b812550d99934acb89d526ed2"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:21812453354b151200034750cd30b0140e82ec2a01fd4357390f67714a1bfbde"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:0634994b026336195778e5693583c060418d4ab453eff21530422690a97e1ee8"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ad4f66fbb893b55f96f03020e67dcab49ffde0177c6565ccf9dec4fdf974eb61"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:2703a9f8f5767986b4f51c259ff452cc837c5a83c8ed5f5361f6e49933743b2f"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bafea6061d63059d8bc2ffc545e2f049221c8a4457d236c5cd6a66678673eab"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f28ae33dc5a0b9cee06e95fd420e42155d83271ab75964baf747ce959cac5f52"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16a534da0e39785687b7295e2fcf9a339f4a20689024983d11afaa4657f8507"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef67fedd863ffffd4adfd46d9d992b0f929c7f61a8307366d664d93517f2c78e"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05c30fd35cc97f14f354916b45feea535d59060ef867446b5c3c7f9b609dd5dc"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c63e3a2e8fb815c4b1f738c284a4579897e37c3cfd95fdb199229a1ccfb638a"}, - {file = "pymongo-4.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e5e193f89f4f8c1fe273f9a6e6df915092c9f2af6db2d1afb8bd53855025c11f"}, - {file = "pymongo-4.6.0-cp39-cp39-win32.whl", hash = "sha256:a09bfb51953930e7e838972ddf646c5d5f984992a66d79da6ba7f6a8d8a890cd"}, - {file = "pymongo-4.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:107a234dc55affc5802acb3b6d83cbb8c87355b38a9457fcd8806bdeb8bce161"}, - {file = "pymongo-4.6.0.tar.gz", hash = "sha256:fb1c56d891f9e34303c451998ef62ba52659648bb0d75b03c5e4ac223a3342c2"}, -] - -[package.dependencies] -dnspython = ">=1.16.0,<3.0.0" - -[package.extras] -aws = ["pymongo-auth-aws (<2.0.0)"] -encryption = ["certifi", "pymongo[aws]", "pymongocrypt (>=1.6.0,<2.0.0)"] -gssapi = ["pykerberos", "winkerberos (>=0.5.0)"] -ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] -snappy = ["python-snappy"] -test = ["pytest (>=7)"] -zstd = ["zstandard"] - -[[package]] -name = "python-dotenv" -version = "1.0.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "pytz" -version = "2023.3.post1" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "sentry-sdk" -version = "1.36.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.36.0.tar.gz", hash = "sha256:f32dd16547f2f45e1c71a96fd4a48925e629541f7ddfe3d5d25ef7d5e94eb3c8"}, - {file = "sentry_sdk-1.36.0-py2.py3-none-any.whl", hash = "sha256:25d574f94fdf72199e331c2401fdac60d01b5be8f32822174c51c3ff0fc2f8cb"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -arq = ["arq (>=0.23)"] -asyncpg = ["asyncpg (>=0.23)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] -grpcio = ["grpcio (>=1.21.1)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -loguru = ["loguru (>=0.5)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "setuptools" -version = "69.0.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, - {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] - -[[package]] -name = "starlette" -version = "0.27.0" -description = "The little ASGI library that shines." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, - {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] - -[[package]] -name = "svgwrite" -version = "1.4.3" -description = "A Python library to create SVG drawings." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "svgwrite-1.4.3-py3-none-any.whl", hash = "sha256:bb6b2b5450f1edbfa597d924f9ac2dd099e625562e492021d7dd614f65f8a22d"}, - {file = "svgwrite-1.4.3.zip", hash = "sha256:a8fbdfd4443302a6619a7f76bc937fc683daf2628d9b737c891ec08b8ce524c3"}, -] - -[[package]] -name = "typing-extensions" -version = "4.8.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, -] - -[[package]] -name = "urllib3" -version = "2.1.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "uvicorn" -version = "0.24.0.post1" -description = "The lightning-fast ASGI server." -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"}, - {file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "uvloop" -version = "0.19.0" -description = "Fast implementation of asyncio event loop on top of libuv" -category = "main" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, - {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, -] - -[package.extras] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] - -[[package]] -name = "virtualenv" -version = "20.24.7" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.24.7-py3-none-any.whl", hash = "sha256:a18b3fd0314ca59a2e9f4b556819ed07183b3e9a3702ecfe213f593d44f7b3fd"}, - {file = "virtualenv-20.24.7.tar.gz", hash = "sha256:69050ffb42419c91f6c1284a7b24e0475d793447e35929b488bf6a0aade39353"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "watchfiles" -version = "0.21.0" -description = "Simple, modern and high performance file watching and code reload in python." -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, - {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, - {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, - {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, - {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, - {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, - {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, - {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, - {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, - {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, - {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, - {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, - {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, - {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, - {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, -] - -[package.dependencies] -anyio = ">=3.0.0" - -[[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - -[[package]] -name = "wrapt" -version = "1.16.0" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.11" -content-hash = "f195a0e14ec30b2323f58379c62a3c727862443ddf4b0f741d93f94ef621498b" diff --git a/frontend/backend/pyproject.toml b/frontend/backend/pyproject.toml deleted file mode 100644 index 37ee2130..00000000 --- a/frontend/backend/pyproject.toml +++ /dev/null @@ -1,36 +0,0 @@ -[tool.poetry] -name = "github-trends" -version = "0.1.0" -description = "" -authors = ["Abhijit Gupta "] -license = "MIT" - -[tool.poetry.dependencies] -python = "^3.11" -fastapi = "^0.104.1" -uvicorn = {extras = ["standard"], version = "^0.24.0.post1"} -requests = "^2.31.0" -python-dotenv = "^1.0.0" -motor = "^3.3.1" -aiofiles = "^23.2.1" -aiounittest = "^1.4.2" -coveralls = "^3.3.1" -grpcio = "^1.59.2" -gunicorn = "^21.2.0" -pymongo = {extras = ["srv"], version = "^4.6.0"} -pytz = "^2023.3.post1" -sentry-sdk = "^1.34.0" -svgwrite = "^1.4.3" - -[tool.poetry.dev-dependencies] - -[tool.poetry.group.dev.dependencies] -black = "^23.11.0" -flake8 = "^6.1.0" -isort = "^5.12.0" -pre-commit = "^3.5.0" -pyinstrument = "^4.6.1" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/frontend/backend/requirements.txt b/frontend/backend/requirements.txt deleted file mode 100644 index 3154fc20..00000000 --- a/frontend/backend/requirements.txt +++ /dev/null @@ -1,39 +0,0 @@ -aiofiles==23.2.1 -aiounittest==1.4.2 -annotated-types==0.6.0 -anyio==3.7.1 -certifi==2023.11.17 -charset-normalizer==3.3.2 -click==8.1.7 -colorama==0.4.6 -coverage==6.5.0 -coveralls==3.3.1 -dnspython==2.4.2 -docopt==0.6.2 -fastapi==0.104.1 -grpcio==1.59.3 -gunicorn==21.2.0 -h11==0.14.0 -httptools==0.6.1 -idna==3.4 -motor==3.3.2 -packaging==23.2 -pydantic-core==2.14.5 -pydantic==2.5.2 -pymongo==4.6.0 -pymongo[srv]==4.6.0 -python-dotenv==1.0.0 -pytz==2023.3.post1 -pyyaml==6.0.1 -requests==2.31.0 -sentry-sdk==1.36.0 -sniffio==1.3.0 -starlette==0.27.0 -svgwrite==1.4.3 -typing-extensions==4.8.0 -urllib3==2.1.0 -uvicorn[standard]==0.24.0.post1 -uvloop==0.19.0 -watchfiles==0.21.0 -websockets==12.0 -wrapt==1.16.0 diff --git a/frontend/backend/scripts/__init__.py b/frontend/backend/scripts/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/scripts/delete_old_data.py b/frontend/backend/scripts/delete_old_data.py deleted file mode 100644 index fcab4171..00000000 --- a/frontend/backend/scripts/delete_old_data.py +++ /dev/null @@ -1,64 +0,0 @@ -import asyncio -import os -import sys -from datetime import datetime -from typing import Any - -from dotenv import find_dotenv, load_dotenv - -load_dotenv(find_dotenv()) - -# Add the parent directory to the Python path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) - -# flake8: noqa E402 - -from src.constants import API_VERSION -from src.data.mongo.main import USER_MONTHS - - -def get_filters(cutoff_date: datetime) -> Any: - return { - "$or": [ - {"month": {"$lte": cutoff_date}}, - {"version": {"$ne": API_VERSION}}, - ], - } - - -async def count_old_rows(cutoff_date: datetime) -> int: - filters = get_filters(cutoff_date) - num_rows = len(await USER_MONTHS.find(filters).to_list(length=None)) # type: ignore - return num_rows - - -async def delete_old_rows(cutoff_date: datetime): - filters = get_filters(cutoff_date) - result = await USER_MONTHS.delete_many(filters) - print(f"Deleted {result.deleted_count} rows") - - -async def main(): - # Replace 'your_date_field' with the actual name of your date field - cutoff_date = datetime(2024, 12, 31) - - count = await count_old_rows(cutoff_date) - if count == 0: - print("No rows to delete.") - return - - print(f"Found {count} rows to delete.") - print() - - confirmation = input("Are you sure you want to delete these rows? (yes/no): ") - if confirmation.lower() != "yes": - print("Operation canceled.") - return - - print() - await delete_old_rows(cutoff_date) - - -if __name__ == "__main__": - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) diff --git a/frontend/backend/scripts/local.py b/frontend/backend/scripts/local.py deleted file mode 100644 index 38665478..00000000 --- a/frontend/backend/scripts/local.py +++ /dev/null @@ -1,104 +0,0 @@ -import argparse -import asyncio -import json -import os -import sys -from datetime import datetime - -# Add the parent directory to the Python path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -os.environ["LOCAL"] = "True" - -# flake8: noqa E402 - -from src.aggregation.layer0 import get_user_data -from src.processing.user import get_top_languages, get_top_repos -from src.processing.wrapped.package import get_wrapped_data - - -def parse_args(): - parser = argparse.ArgumentParser(description="GitHub Trends Script") - - parser.add_argument("--user_id", required=True, help="GitHub user ID", type=str) - parser.add_argument( - "--access_token", required=True, help="GitHub access token", type=str - ) - parser.add_argument( - "--start_date", - default="2023-01-01", - help="Start date in YYYY-MM-DD format", - type=str, - ) - parser.add_argument( - "--end_date", - default="2023-01-31", - help="End date in YYYY-MM-DD format", - type=str, - ) - parser.add_argument( - "--timezone", default="America/New_York", help="Timezone", type=str - ) - parser.add_argument( - "--output_dir", default="./", help="Output directory path", type=str - ) - - return parser.parse_args() - - -async def main(): - args = parse_args() - - start_date = datetime.strptime(args.start_date, "%Y-%m-%d") - end_date = datetime.strptime(args.end_date, "%Y-%m-%d") - - print("Local script running...") - print("User ID:", args.user_id) - print("Access token:", args.access_token) - print("Start date:", start_date) - print("End date:", end_date) - print("Timezone:", args.timezone) - print("Output directory:", args.output_dir) - print() - - raw_output = await get_user_data( - args.user_id, start_date, end_date, args.timezone, args.access_token - ) - - with open(os.path.join(args.output_dir, "raw.json"), "w") as f: - f.write(raw_output.model_dump_json(indent=2)) - - langs_output = get_top_languages( - raw_output, loc_metric="changed", include_private=True - ) - - langs_output = ( - [json.loads(x.model_dump_json()) for x in langs_output[0]], - langs_output[1], - ) - - repos_output = get_top_repos( - raw_output, loc_metric="changed", include_private=True, group="none" - ) - - repos_output = ( - [json.loads(x.model_dump_json()) for x in repos_output[0]], - repos_output[1], - ) - - with open(os.path.join(args.output_dir, "langs.json"), "w") as f: - f.write(json.dumps(langs_output, indent=2)) - - with open(os.path.join(args.output_dir, "repos.json"), "w") as f: - f.write(json.dumps(repos_output, indent=2)) - - wrapped_user = get_wrapped_data(raw_output, 2023) - - with open(os.path.join(args.output_dir, "wrapped.json"), "w") as f: - f.write(wrapped_user.model_dump_json(indent=2)) - - print("Wrote output to", args.output_dir) - - -if __name__ == "__main__": - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) diff --git a/frontend/backend/src/__init__.py b/frontend/backend/src/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/aggregation/layer0/__init__.py b/frontend/backend/src/aggregation/layer0/__init__.py deleted file mode 100644 index 9a4ac758..00000000 --- a/frontend/backend/src/aggregation/layer0/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from src.aggregation.layer0.package import get_user_data - -__all__ = ["get_user_data"] diff --git a/frontend/backend/src/aggregation/layer0/contributions.py b/frontend/backend/src/aggregation/layer0/contributions.py deleted file mode 100644 index 9514a9aa..00000000 --- a/frontend/backend/src/aggregation/layer0/contributions.py +++ /dev/null @@ -1,498 +0,0 @@ -from collections import defaultdict -from datetime import date, datetime -from typing import Any, Dict, List, Optional, Tuple, Union - -import pytz - -from src.aggregation.layer0.languages import CommitLanguages, get_commit_languages -from src.constants import ( - GRAPHQL_NODE_CHUNK_SIZE, - GRAPHQL_NODE_THREADS, - NODE_QUERIES, - PR_FILES, - REST_NODE_THREADS, -) -from src.data.github.graphql import ( - RawCalendar, - RawCommit as GraphQLRawCommit, - RawEventsCommit, - RawEventsEvent, - RawRepo, - get_commits, - get_repo, - get_user_contribution_calendar, - get_user_contribution_events, -) -from src.data.github.rest import ( - RawCommit as RESTRawCommit, - RawCommitFile, - get_commit_files, - get_repo_commits, -) -from src.models import UserContributions -from src.utils import date_to_datetime, gather - - -class ContribsList: - def __init__(self): - self.commits: List[RawEventsCommit] = [] - self.issues: List[RawEventsEvent] = [] - self.prs: List[RawEventsEvent] = [] - self.reviews: List[RawEventsEvent] = [] - self.repos: List[RawEventsEvent] = [] - - def add(self, label: str, event: Union[RawEventsCommit, RawEventsEvent]): - if label == "commit" and isinstance(event, RawEventsCommit): - self.commits.append(event) - elif label == "issue" and isinstance(event, RawEventsEvent): - self.issues.append(event) - elif label == "pr" and isinstance(event, RawEventsEvent): - self.prs.append(event) - elif label == "review" and isinstance(event, RawEventsEvent): - self.reviews.append(event) - elif label == "repo" and isinstance(event, RawEventsEvent): - self.repos.append(event) - - -def get_user_all_contribution_events( - user_id: str, - start_date: datetime, - end_date: datetime, - access_token: Optional[str] = None, -) -> Dict[str, ContribsList]: - repo_contribs: Dict[str, ContribsList] = defaultdict(lambda: ContribsList()) - after: Optional[str] = "" - cont = True - while cont: - after_str = after if isinstance(after, str) else "" - response = get_user_contribution_events( - user_id=user_id, - start_date=start_date, - end_date=end_date, - after=after_str, - access_token=access_token, - ) - - cont = False - node_lists = [ - ("commit", response.commit_contribs_by_repo), - ("issue", response.issue_contribs_by_repo), - ("pr", response.pr_contribs_by_repo), - ("review", response.review_contribs_by_repo), - ] - for event_type, event_list in node_lists: - for repo in event_list: - name = repo.repo.name - for event in repo.contribs.nodes: - repo_contribs[name].add(event_type, event) - if repo.contribs.page_info.has_next_page: - after = repo.contribs.page_info.end_cursor - cont = True - - for repo in response.repo_contribs.nodes: - name = repo.repo.name - node = RawEventsEvent(occurredAt=repo.occurred_at) - repo_contribs[name].add("repo", node) - - return repo_contribs - - -def get_all_commit_info( - user_id: str, - name_with_owner: str, - start_date: datetime, - end_date: datetime, - access_token: Optional[str] = None, -) -> List[RESTRawCommit]: - owner, repo = name_with_owner.split("/") - data: List[RESTRawCommit] = [] - for i in range(10): - if len(data) == 100 * i: - new_data = get_repo_commits( - owner, repo, user_id, start_date, end_date, i + 1, access_token - ) - data.extend(new_data) - - # sort ascending - return sorted(data, key=lambda x: x.timestamp) - - -async def get_all_commit_languages( - commit_infos: List[List[RESTRawCommit]], - repos: List[str], - repo_infos: Dict[str, RawRepo], - access_token: Optional[str] = None, - catch_errors: bool = False, -) -> Tuple[Dict[str, List[datetime]], Dict[str, List[CommitLanguages]]]: - commit_node_ids = [[x.node_id for x in repo] for repo in commit_infos] - commit_times = [[x.timestamp for x in repo] for repo in commit_infos] - - id_mapping: Dict[str, Tuple[int, int]] = {} - repo_mapping: Dict[str, str] = {} - all_node_ids: List[str] = [] - for i, repo_node_ids in enumerate(commit_node_ids): - for j, node_id in enumerate(repo_node_ids): - id_mapping[node_id] = (i, j) - repo_mapping[node_id] = repos[i] - all_node_ids.append(node_id) - - node_id_chunks: List[List[str]] = [ - all_node_ids[i : min(len(all_node_ids), i + GRAPHQL_NODE_CHUNK_SIZE)] - for i in range(0, len(all_node_ids), GRAPHQL_NODE_CHUNK_SIZE) - ] - - commit_language_chunks: List[List[Optional[GraphQLRawCommit]]] = await gather( - funcs=[get_commits for _ in node_id_chunks], - args_dicts=[ - { - "node_ids": node_id_chunk, - "access_token": access_token, - "catch_errors": catch_errors, - } - for node_id_chunk in node_id_chunks - ], - max_threads=GRAPHQL_NODE_THREADS, - ) - - temp_commit_languages: List[Optional[GraphQLRawCommit]] = [] - for commit_language_chunk in commit_language_chunks: - temp_commit_languages.extend(commit_language_chunk) - - # returns commits with no associated PR or incomplete PR - filtered_commits: List[GraphQLRawCommit] = filter( - lambda x: x is not None - and (len(x.prs.nodes) == 0 or x.prs.nodes[0].changed_files > PR_FILES) - and (x.additions + x.deletions > 100), - temp_commit_languages, - ) # type: ignore - - # get NODE_QUERIES largest commits with no associated PR or incomplete PR - sorted_commits = sorted( - filtered_commits, key=lambda x: x.additions + x.deletions, reverse=True - )[:NODE_QUERIES] - - sorted_commit_urls = [commit.url.split("/") for commit in sorted_commits] - commit_files: List[List[RawCommitFile]] = await gather( - funcs=[get_commit_files for _ in sorted_commit_urls], - args_dicts=[ - { - "owner": url[3], - "repo": url[4], - "sha": url[6], - "access_token": access_token, - } - for url in sorted_commit_urls - ], - max_threads=REST_NODE_THREADS, - ) - - commit_files_dict: Dict[str, List[RawCommitFile]] = { - commit.url: commit_file - for commit, commit_file in zip(sorted_commits, commit_files) - } - - commit_languages: List[List[CommitLanguages]] = [ - [CommitLanguages() for _ in repo] for repo in commit_infos - ] - for raw_commits, node_ids in zip(commit_language_chunks, node_id_chunks): - for raw_commit, node_id in zip(raw_commits, node_ids): - curr_commit_files: Optional[List[RawCommitFile]] = None - if raw_commit is not None and raw_commit.url in commit_files_dict: - curr_commit_files = commit_files_dict[raw_commit.url] - lang_breakdown = get_commit_languages( - raw_commit, curr_commit_files, repo_infos[repo_mapping[node_id]] - ) - i, j = id_mapping[node_id] - commit_languages[i][j] = lang_breakdown - - commit_times_dict: Dict[str, List[datetime]] = {} - commit_languages_dict: Dict[str, List[CommitLanguages]] = {} - for repo, times, languages in zip(repos, commit_times, commit_languages): - commit_times_dict[repo] = times - commit_languages_dict[repo] = languages - - return commit_times_dict, commit_languages_dict - - -async def get_cleaned_contributions( - user_id: str, - start_date: datetime, - end_date: datetime, - access_token: Optional[str], - catch_errors: bool = False, -) -> Tuple[ - RawCalendar, - Dict[str, ContribsList], - Dict[str, RawRepo], - Dict[str, List[datetime]], - Dict[str, List[CommitLanguages]], -]: - calendar = get_user_contribution_calendar( - user_id, start_date, end_date, access_token - ) - contrib_events = get_user_all_contribution_events( - user_id, start_date, end_date, access_token - ) - repos: List[str] = list(set(contrib_events.keys())) - - commit_infos: List[List[RESTRawCommit]] = await gather( - funcs=[get_all_commit_info for _ in repos], - args_dicts=[ - { - "user_id": user_id, - "name_with_owner": repo, - "start_date": start_date, - "end_date": end_date, - "access_token": access_token, - } - for repo in repos - ], - max_threads=REST_NODE_THREADS, - ) - - _repo_infos: List[Optional[RawRepo]] = await gather( - funcs=[get_repo for _ in repos], - args_dicts=[ - { - "owner": repo.split("/")[0], - "repo": repo.split("/")[1], - "access_token": access_token, - "catch_errors": catch_errors, - } - for repo in repos - ], - max_threads=REST_NODE_THREADS, - ) - - repo_infos = {k: v for k, v in zip(repos, _repo_infos) if v is not None} - - commit_times_dict, commit_languages_dict = await get_all_commit_languages( - commit_infos, - repos, - repo_infos, - access_token, - catch_errors, - ) - - return ( - calendar, - contrib_events, - repo_infos, - commit_times_dict, - commit_languages_dict, - ) - - -class StatsContainer: - def __init__(self): - self.contribs: int = 0 - self.commits: int = 0 - self.issues: int = 0 - self.prs: int = 0 - self.reviews: int = 0 - self.repos: int = 0 - self.other: int = 0 - self.languages = CommitLanguages() - - def add_stat(self, label: str, count: int, add: bool = False) -> None: - if label == "commit": - self.commits += count - elif label == "issue": - self.issues += count - elif label == "pr": - self.prs += count - elif label == "review": - self.reviews += count - elif label == "repo": - self.repos += count - - if add: - self.contribs += count - else: - self.other -= count - - def to_dict(self) -> Dict[str, Any]: - return { - "contribs_count": self.contribs, - "commits_count": self.commits, - "issues_count": self.issues, - "prs_count": self.prs, - "reviews_count": self.reviews, - "repos_count": self.repos, - "other_count": self.other, - "languages": self.languages.to_dict(), - } - - -class ListsContainer: - def __init__(self): - self.commits: List[datetime] = [] - self.issues: List[datetime] = [] - self.prs: List[datetime] = [] - self.reviews: List[datetime] = [] - self.repos: List[datetime] = [] - - def add_list(self, label: str, times: List[datetime]) -> None: - if label == "commit": - self.commits.extend(times) - elif label == "issue": - self.issues.extend(times) - elif label == "pr": - self.prs.extend(times) - elif label == "review": - self.reviews.extend(times) - elif label == "repo": - self.repos.extend(times) - - def to_dict(self) -> Dict[str, Any]: - return { - "commits": self.commits, - "issues": self.issues, - "prs": self.prs, - "reviews": self.reviews, - "repos": self.repos, - } - - -class DateContainer: - def __init__(self): - self.date = "" - self.weekday = 0 - self.stats = StatsContainer() - self.lists = ListsContainer() - - def add_stat( - self, label: str, count: int, times: List[datetime], add: bool = False - ): - self.stats.add_stat(label, count, add) - self.lists.add_list(label, times) - - def to_dict(self) -> Dict[str, Any]: - return { - "date": self.date, - "weekday": self.weekday, - "stats": self.stats.to_dict(), - "lists": self.lists.to_dict(), - } - - -# assumed one month span, can be no more than one year -async def get_contributions( - user_id: str, - start_date: date, - end_date: date, - timezone_str: str = "US/Eastern", - access_token: Optional[str] = None, - catch_errors: bool = False, -) -> UserContributions: - tz = pytz.timezone(timezone_str) - - start_month = date_to_datetime(start_date) - end_month = date_to_datetime(end_date, hour=23, minute=59, second=59) - ( - calendar, - contrib_events, - repo_infos, - commit_times_dict, - commit_languages_dict, - ) = await get_cleaned_contributions( - user_id, start_month, end_month, access_token, catch_errors - ) - - total_stats = StatsContainer() - public_stats = StatsContainer() - total: Dict[str, DateContainer] = defaultdict(DateContainer) - public: Dict[str, DateContainer] = defaultdict(DateContainer) - repo_stats: Dict[str, StatsContainer] = defaultdict(StatsContainer) - repositories: Dict[str, Dict[str, DateContainer]] = defaultdict( - lambda: defaultdict(DateContainer) - ) - - for week in calendar.weeks: - for day in week.contribution_days: - day_str = str(day.date) - for obj, stats_obj in [(total, total_stats), (public, public_stats)]: - obj[day_str].date = day.date.isoformat() - obj[day_str].weekday = day.weekday - obj[day_str].stats.contribs = day.count - obj[day_str].stats.other = day.count - stats_obj.contribs += day.count - stats_obj.other += day.count - - def update_stats( - date_str: str, repo: str, event: str, count: int, times: List[datetime] - ): - # update global counts for this event - total[date_str].add_stat(event, count, times) - total_stats.add_stat(event, count) - if not repo_infos[repo].is_private: - public[date_str].add_stat(event, count, times) - public_stats.add_stat(event, count) - repositories[repo][date_str].add_stat(event, count, times, add=True) - repo_stats[repo].add_stat(event, count, add=True) - - def update_langs(date_str: str, repo: str, langs: CommitLanguages): - stores = [ - total[date_str].stats.languages, - total_stats.languages, - repositories[repo][date_str].stats.languages, - repo_stats[repo].languages, - ] - if not repo_infos[repo].is_private: - stores.append(public[date_str].stats.languages) - stores.append(public_stats.languages) - - for store in stores: - store += langs - - for repo, repo_events in contrib_events.items(): - for label, events in [ - ("commit", repo_events.commits), - ("issue", repo_events.issues), - ("pr", repo_events.prs), - ("review", repo_events.reviews), - ("repo", repo_events.repos), - ]: - events = sorted(events, key=lambda x: x.occurred_at) - for event in events: - datetime_obj = event.occurred_at.astimezone(tz) - date_str = datetime_obj.date().isoformat() - repositories[repo][date_str].date = date_str - if isinstance(event, RawEventsCommit): - count = 0 - commit_times: List[datetime] = [] - while len(commit_languages_dict[repo]) > 0 and count < event.count: - commit_times.append(commit_times_dict[repo].pop(0)) - langs = commit_languages_dict[repo].pop(0) - update_langs(date_str, repo, langs) - count += 1 - update_stats(date_str, repo, "commit", event.count, commit_times) - else: - update_stats(date_str, repo, label, 1, [datetime_obj]) - - total_stats_dict = total_stats.to_dict() - public_stats_dict = public_stats.to_dict() - repo_stats_dict = {name: stats.to_dict() for name, stats in repo_stats.items()} - - for repo in repo_stats: - repo_stats_dict[repo]["private"] = repo_infos[repo].is_private - - total_list = [v.to_dict() for v in total.values() if v.stats.contribs > 0] - public_list = [v.to_dict() for v in public.values() if v.stats.contribs > 0] - repositories_list = { - name: [v.to_dict() for v in repo.values()] - for name, repo in repositories.items() - } - - output = UserContributions.model_validate( - { - "total_stats": total_stats_dict, - "public_stats": public_stats_dict, - "total": total_list, - "public": public_list, - "repo_stats": repo_stats_dict, - "repos": repositories_list, - } - ) - - return output diff --git a/frontend/backend/src/aggregation/layer0/follows.py b/frontend/backend/src/aggregation/layer0/follows.py deleted file mode 100644 index 77dbe345..00000000 --- a/frontend/backend/src/aggregation/layer0/follows.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import List, Optional - -from src.data.github.graphql import ( - get_user_followers as _get_user_followers, - get_user_following as _get_user_following, -) -from src.models import User, UserFollows - - -def get_user_follows(user_id: str, access_token: Optional[str]) -> UserFollows: - """get user followers and users following for given user""" - - followers: List[User] = [] - following: List[User] = [] - - for user_list, get_func in zip( - [followers, following], [_get_user_followers, _get_user_following] - ): - after: Optional[str] = "" - index, cont = 0, True # initialize variables - while cont and index < 10: - after_str: str = after if isinstance(after, str) else "" - data = get_func(user_id, after=after_str, access_token=access_token) - - cont = False - - user_list.extend( - map( - lambda x: User(name=x.name, login=x.login, url=x.url), - data.nodes, - ) - ) - if data.page_info.has_next_page: - after = data.page_info.end_cursor - cont = True - - index += 1 - - return UserFollows(followers=followers, following=following) diff --git a/frontend/backend/src/aggregation/layer0/languages.py b/frontend/backend/src/aggregation/layer0/languages.py deleted file mode 100644 index bb3a37c4..00000000 --- a/frontend/backend/src/aggregation/layer0/languages.py +++ /dev/null @@ -1,118 +0,0 @@ -from json import load -from typing import Any, Dict, List, Optional, Union - -from src.constants import BLACKLIST, CUTOFF, DEFAULT_COLOR, FILE_CUTOFF -from src.data.github.graphql import RawCommit, RawRepo -from src.data.github.rest import RawCommitFile - -EXTENSIONS: Dict[str, Dict[str, str]] = load(open("./src/data/github/extensions.json")) - - -class CommitLanguages: - def __init__(self): - self.langs: Dict[str, Dict[str, Union[str, int]]] = {} - - def __repr__(self): - return str(self.langs) - - def add_lines( - self, name: str, color: Optional[str], additions: int, deletions: int - ): - if ( - name not in BLACKLIST - and max(additions, deletions) > 0 - and max(additions, deletions) < FILE_CUTOFF - ): - color = color or DEFAULT_COLOR - if name not in self.langs: - self.langs[name] = {"color": color, "additions": 0, "deletions": 0} - self.langs[name]["additions"] += additions # type: ignore - self.langs[name]["deletions"] += deletions # type: ignore - - def normalize(self, add_ratio: float, del_ratio: float): - for lang in self.langs: - new_add = round(int(self.langs[lang]["additions"]) * add_ratio) - self.langs[lang]["additions"] = new_add - new_del = round(int(self.langs[lang]["deletions"]) * del_ratio) - self.langs[lang]["deletions"] = new_del - - def __add__(self, other: "CommitLanguages"): - for lang in other.langs: - if lang not in self.langs: - self.langs[lang] = other.langs[lang].copy() - else: - self.langs[lang]["additions"] += other.langs[lang]["additions"] # type: ignore - self.langs[lang]["deletions"] += other.langs[lang]["deletions"] # type: ignore - - def to_dict(self) -> Dict[str, Any]: - return self.langs - - -def get_commit_languages( - commit: Optional[RawCommit], - files: Optional[List[RawCommitFile]], - repo: RawRepo, -) -> CommitLanguages: - out = CommitLanguages() - - if commit is None: - return out - - if max(commit.additions, commit.deletions) == 0: - return out - - # assummed to be auto-generated or copied - if max(commit.additions, commit.deletions) > 10 * CUTOFF or ( - max(commit.additions, commit.deletions) > CUTOFF - and min(commit.additions, commit.deletions) == 0 - ): - return out - - pr_coverage = 0 - if len(commit.prs.nodes) > 0: - pr_obj = commit.prs.nodes[0] - pr_files = pr_obj.files.nodes - total_changed = sum(x.additions + x.deletions for x in pr_files) - pr_coverage = total_changed / max(1, (pr_obj.additions + pr_obj.deletions)) - - if files is not None: - for file in files: - filename = file.filename.split(".") - extension = "" if len(filename) <= 1 else filename[-1] - lang = EXTENSIONS.get(f".{extension}", None) - if lang is not None: - out.add_lines( - lang["name"], lang["color"], file.additions, file.deletions - ) - elif len(commit.prs.nodes) > 0 and pr_coverage > 0.25: - pr = commit.prs.nodes[0] - total_additions, total_deletions = 0, 0 - for file in pr.files.nodes: - filename = file.path.split(".") - extension = "" if len(filename) <= 1 else filename[-1] - lang = EXTENSIONS.get(f".{extension}", None) - if lang is not None: - out.add_lines( - lang["name"], lang["color"], file.additions, file.deletions - ) - total_additions += file.additions - total_deletions += file.deletions - add_ratio = min(pr.additions, commit.additions) / max(1, total_additions) - del_ratio = min(pr.deletions, commit.deletions) / max(1, total_deletions) - out.normalize(add_ratio, del_ratio) - elif commit.additions + commit.deletions > 2 * CUTOFF: - # assummed to be auto generated - return out - else: - repo_info = repo.languages.edges - languages = [x for x in repo_info if x.node.name not in BLACKLIST] - total_repo_size = sum(language.size for language in languages) - for language in languages: - lang_name = language.node.name - lang_color = language.node.color - lang_size = language.size - additions = round(commit.additions * lang_size / total_repo_size) - deletions = round(commit.deletions * lang_size / total_repo_size) - out.add_lines(lang_name, lang_color, additions, deletions) - - return out diff --git a/frontend/backend/src/aggregation/layer0/package.py b/frontend/backend/src/aggregation/layer0/package.py deleted file mode 100644 index 469996ff..00000000 --- a/frontend/backend/src/aggregation/layer0/package.py +++ /dev/null @@ -1,28 +0,0 @@ -from datetime import date -from typing import Optional - -from src.aggregation.layer0.contributions import get_contributions -from src.models import UserPackage - -# from src.subscriber.aggregation.user.follows import get_user_follows - - -async def get_user_data( - user_id: str, - start_date: date, - end_date: date, - timezone_str: str, - access_token: Optional[str], - catch_errors: bool = False, -) -> UserPackage: - """packages all processing steps for the user query""" - - contribs = await get_contributions( - user_id=user_id, - start_date=start_date, - end_date=end_date, - timezone_str=timezone_str, - access_token=access_token, - catch_errors=catch_errors, - ) - return UserPackage(contribs=contribs) diff --git a/frontend/backend/src/aggregation/layer1/__init__.py b/frontend/backend/src/aggregation/layer1/__init__.py deleted file mode 100644 index 204d5445..00000000 --- a/frontend/backend/src/aggregation/layer1/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from src.aggregation.layer1.user import query_user - -__all__ = ["query_user"] diff --git a/frontend/backend/src/aggregation/layer1/auth.py b/frontend/backend/src/aggregation/layer1/auth.py deleted file mode 100644 index 31a2006c..00000000 --- a/frontend/backend/src/aggregation/layer1/auth.py +++ /dev/null @@ -1,61 +0,0 @@ -from datetime import timedelta -from typing import List, Optional, Tuple - -from src.constants import OWNER, REPO -from src.data.github.rest import ( - RESTError, - RESTErrorNotFound, - get_repo_stargazers as github_get_repo_stargazers, - get_user as github_get_user, - get_user_starred_repos as github_get_user_starred_repos, -) -from src.data.github.utils import get_access_token -from src.data.mongo.user import get_public_user as db_get_public_user -from src.utils import alru_cache - - -async def get_valid_github_user(user_id: str) -> Optional[str]: - access_token = get_access_token() - try: - return github_get_user(user_id, access_token)["login"] - except (RESTErrorNotFound, KeyError): - # User does not exist - return None - except RESTError: - # Rate limited, so assume user exists - return user_id - - -async def get_valid_db_user(user_id: str) -> bool: - user = await db_get_public_user(user_id) - return user is not None - - -@alru_cache(ttl=timedelta(minutes=15)) -async def get_repo_stargazers( - owner: str = OWNER, repo: str = REPO, no_cache: bool = False -) -> Tuple[bool, List[str]]: - access_token = get_access_token() - data: List[str] = [] - page = 0 - while len(data) == 100 * page: - temp_data = github_get_repo_stargazers(access_token, owner, repo, page=page) - temp_data = [x["user"]["login"] for x in temp_data] - data.extend(temp_data) - page += 1 - - return (True, data) - - -async def get_user_stars(user_id: str) -> List[str]: - access_token = get_access_token() - try: - data = github_get_user_starred_repos(user_id, access_token) - data = [x["repo"]["full_name"] for x in data] - return data - except RESTErrorNotFound: - # User does not exist (and rate limited previously) - return [] - except RESTError: - # Rate limited, so assume user starred repo - return [f"{OWNER}/{REPO}"] diff --git a/frontend/backend/src/aggregation/layer1/user.py b/frontend/backend/src/aggregation/layer1/user.py deleted file mode 100644 index 59dc4ea4..00000000 --- a/frontend/backend/src/aggregation/layer1/user.py +++ /dev/null @@ -1,125 +0,0 @@ -from calendar import monthrange -from datetime import date, datetime, timedelta -from typing import List, Optional, Tuple - -import requests - -from src.aggregation.layer0.package import get_user_data -from src.constants import API_VERSION # , BACKEND_URL, PROD -from src.data.github.graphql import GraphQLErrorRateLimit -from src.data.mongo.secret import update_keys -from src.data.mongo.user_months import UserMonth, get_user_months, set_user_month -from src.models.user.main import UserPackage -from src.utils import alru_cache, date_to_datetime - -s = requests.Session() - - -# Formerly the subscriber, compute and save new data here - - -async def query_user_month( - user_id: str, - access_token: Optional[str], - private_access: bool, - start_date: date, - retries: int = 0, -) -> Optional[UserMonth]: - year, month = start_date.year, start_date.month - end_day = monthrange(year, month)[1] - end_date = date(year, month, end_day) - - try: - data = await get_user_data( - user_id=user_id, - start_date=start_date, - end_date=end_date, - timezone_str="US/Eastern", - access_token=access_token, - catch_errors=retries > 0, - ) - except GraphQLErrorRateLimit: - return None - except Exception: - # Retry, catching exceptions and marking incomplete this time - if retries < 1: - await query_user_month( - user_id, access_token, private_access, start_date, retries + 1 - ) - return None - - month_completed = datetime.now() > date_to_datetime(end_date) + timedelta(days=1) - user_month = UserMonth.model_validate( - { - "user_id": user_id, - "month": date_to_datetime(start_date), - "version": API_VERSION, - "private": private_access, - "complete": retries == 0 and month_completed, - "data": data, - } - ) - - await set_user_month(user_month) - return user_month - - -@alru_cache(ttl=timedelta(hours=6)) -async def query_user( - user_id: str, - access_token: Optional[str], - private_access: bool = False, - start_date: date = date.today() - timedelta(365), - end_date: date = date.today(), - max_time: int = 3600, # seconds - no_cache: bool = False, -) -> Tuple[bool, UserPackage]: - # Return (possibly incomplete) within max_time seconds - start_time = datetime.now() - incomplete = False - - await update_keys() - - curr_data: List[UserMonth] = await get_user_months( - user_id, private_access, start_date, end_date - ) - curr_months = [x.month for x in curr_data if x.complete] - - month, year = start_date.month, start_date.year - new_months: List[date] = [] - while date(year, month, 1) <= end_date: - start = date(year, month, 1) - if date_to_datetime(start) not in curr_months: - new_months.append(start) - month = month % 12 + 1 - year = year + (month == 1) - - # Start with complete months and add any incomplete months - all_user_packages: List[UserPackage] = [x.data for x in curr_data if x.complete] - for month in new_months: - if datetime.now() - start_time < timedelta(seconds=max_time): - temp = await query_user_month(user_id, access_token, private_access, month) - if temp is not None: - all_user_packages.append(temp.data) - else: - incomplete = True - else: - incomplete = True - - out: UserPackage = UserPackage.empty() - if len(all_user_packages) > 0: - out = all_user_packages[0] - for user_package in all_user_packages[1:]: - out += user_package - out.incomplete = incomplete - - if incomplete or len(new_months) > 1: - # TODO: figure out why this causes an infinite loop - # # cache buster for publisher - # if PROD: - # s.get(f"{BACKEND_URL}/user/{user_id}?no_cache=True") - - return (False, out) - - # only cache if just the current month updated - return (True, out) diff --git a/frontend/backend/src/aggregation/layer2/__init__.py b/frontend/backend/src/aggregation/layer2/__init__.py deleted file mode 100644 index 13c2a315..00000000 --- a/frontend/backend/src/aggregation/layer2/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from src.aggregation.layer2.auth import get_is_valid_user -from src.aggregation.layer2.user import get_user, get_user_demo - -__all__ = ["get_is_valid_user", "get_user", "get_user_demo"] diff --git a/frontend/backend/src/aggregation/layer2/auth.py b/frontend/backend/src/aggregation/layer2/auth.py deleted file mode 100644 index 3311b6c1..00000000 --- a/frontend/backend/src/aggregation/layer2/auth.py +++ /dev/null @@ -1,58 +0,0 @@ -from datetime import timedelta -from typing import Optional, Tuple - -from src.aggregation.layer1.auth import ( - get_repo_stargazers, - get_user_stars, - get_valid_db_user, - get_valid_github_user, -) -from src.constants import OWNER, REPO, USER_BLACKLIST, USER_WHITELIST -from src.data.github.rest import RESTError -from src.utils import alru_cache - - -async def check_github_user_exists(user_id: str) -> Optional[str]: - return await get_valid_github_user(user_id) - - -async def check_db_user_exists(user_id: str) -> bool: - return await get_valid_db_user(user_id) - - -async def check_user_starred_repo( - user_id: str, owner: str = OWNER, repo: str = REPO -) -> bool: - return True - # Checks the repo's starred users (with cache) - try: - repo_stargazers = await get_repo_stargazers(owner, repo) - if user_id in repo_stargazers: - return True - except RESTError: - return True # Assume the user has starred the repo - - # Checks the user's 30 most recent starred repos (no cache) - user_stars = await get_user_stars(user_id) - return f"{owner}/{repo}" in user_stars - - -@alru_cache(ttl=timedelta(hours=1)) -async def get_is_valid_user(user_id: str) -> Tuple[bool, str]: - if user_id.lower() in USER_BLACKLIST: - # TODO: change error message - return (False, "GitHub user not found") - - if user_id.lower() in USER_WHITELIST: - return (True, f"Valid user {user_id.lower()}") - - valid_user_id = await check_github_user_exists(user_id) - if valid_user_id is None: - return (False, "GitHub user not found") - - valid_db_user = await check_db_user_exists(valid_user_id) - user_starred = await check_user_starred_repo(valid_user_id) - if not (user_starred or valid_db_user): - return (False, "Repo not starred") - - return (True, f"Valid user {valid_user_id}") diff --git a/frontend/backend/src/aggregation/layer2/user.py b/frontend/backend/src/aggregation/layer2/user.py deleted file mode 100644 index cf256601..00000000 --- a/frontend/backend/src/aggregation/layer2/user.py +++ /dev/null @@ -1,79 +0,0 @@ -from datetime import date, timedelta -from typing import Optional, Tuple - -from src.aggregation.layer0 import get_user_data -from src.constants import USER_BLACKLIST -from src.data.mongo.secret.functions import update_keys -from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user -from src.data.mongo.user_months import get_user_months -from src.models import UserPackage -from src.models.background import UpdateUserBackgroundTask -from src.utils import alru_cache - -# Formerly the publisher, loads existing data here - - -async def _get_user( - user_id: str, private_access: bool, start_date: date, end_date: date -) -> Tuple[Optional[UserPackage], bool]: - user_months = await get_user_months(user_id, private_access, start_date, end_date) - if len(user_months) == 0: - return None, False - - expected_num_months = ( - (end_date.year - start_date.year) * 12 + (end_date.month - start_date.month) + 1 - ) - complete = len(user_months) == expected_num_months - - user_data = user_months[0].data - for user_month in user_months[1:]: - user_data += user_month.data - - # TODO: handle timezone_str here - return user_data.trim(start_date, end_date), complete - - -@alru_cache() -async def get_user( - user_id: str, - start_date: date, - end_date: date, - no_cache: bool = False, -) -> Tuple[ - bool, Tuple[Optional[UserPackage], bool, Optional[UpdateUserBackgroundTask]] -]: - if user_id in USER_BLACKLIST: - return (False, (None, False, None)) - - user: Optional[PublicUserModel] = await db_get_public_user(user_id) - if user is None: - return (False, (None, False, None)) - - private_access = user.private_access or False - user_data, complete = await _get_user(user_id, private_access, start_date, end_date) - background_task = UpdateUserBackgroundTask( - user_id=user_id, - access_token=user.access_token, - private_access=private_access, - start_date=start_date, - end_date=end_date, - ) - return (complete, (user_data, complete, background_task)) - - -@alru_cache(ttl=timedelta(minutes=15)) -async def get_user_demo( - user_id: str, start_date: date, end_date: date, no_cache: bool = False -) -> Tuple[bool, UserPackage]: - await update_keys() - timezone_str = "US/Eastern" - # recompute/cache but don't save to db - data = await get_user_data( - user_id=user_id, - start_date=start_date, - end_date=end_date, - timezone_str=timezone_str, - access_token=None, - catch_errors=True, - ) - return (True, data) diff --git a/frontend/backend/src/constants.py b/frontend/backend/src/constants.py deleted file mode 100644 index 9066fe05..00000000 --- a/frontend/backend/src/constants.py +++ /dev/null @@ -1,74 +0,0 @@ -import os - -# GLOBAL -LOCAL = os.getenv("LOCAL", "False") == "True" -PROD = os.getenv("PROD", "False") == "True" -PROJECT_ID = "github-334619" -BACKEND_URL = "https://api.githubtrends.io" if PROD else "http://localhost:8000" - -OWNER = "avgupta456" -REPO = "github-trends" - -# API -# https://docs.github.com/en/rest/reference/rate-limit -# https://docs.github.com/en/rest/guides/best-practices-for-integrators#dealing-with-secondary-rate-limits -# https://docs.github.com/en/graphql/overview/resource-limitations -TIMEOUT = 15 # max seconds to wait for api response -GRAPHQL_NODE_CHUNK_SIZE = 50 # number of nodes (commits) to query (max 100) -GRAPHQL_NODE_THREADS = 5 # number of node queries simultaneously (avoid blacklisting) -REST_NODE_THREADS = 50 # number of node queries simultaneously (avoid blacklisting) - -PR_FILES = 5 # max number of files to query for PRs -NODE_QUERIES = 20 # max number of node queries to make -CUTOFF = 1000 # if additions or deletions > CUTOFF, or sum > 2 * CUTOFF, ignore LOC -FILE_CUTOFF = 1000 # if less than cutoff in file, count LOC - -API_VERSION = 0.02 # determines when to overwrite MongoDB data - -# CUSTOMIZATION -BLACKLIST = ["Jupyter Notebook", "HTML"] # languages to ignore - -# OAUTH -prefix = "PROD" if PROD else "DEV" -# client ID for GitHub OAuth App -OAUTH_CLIENT_ID = os.getenv(f"{prefix}_OAUTH_CLIENT_ID", "") -# client secret for App -OAUTH_CLIENT_SECRET = os.getenv(f"{prefix}_OAUTH_CLIENT_SECRET", "") -# redirect uri for App -OAUTH_REDIRECT_URI = os.getenv(f"{prefix}_OAUTH_REDIRECT_URI", "") - -# MONGODB -MONGODB_PASSWORD = os.getenv("MONGODB_PASSWORD", "") - -# SVG -DEFAULT_COLOR = "#858585" - -# SENTRY -SENTRY_DSN = os.getenv("SENTRY_DSN", "") - -# TESTING -TEST_USER_ID = "avgupta456" -TEST_REPO = "github-trends" -TEST_TOKEN = os.getenv("AUTH_TOKEN", "") # for authentication -TEST_NODE_IDS = [ - "C_kwDOENp939oAKGM1MzdlM2QzMTZjMmEyZGIyYWU4ZWI0MmNmNjQ4YWEwNWQ5OTBiMjM", - "C_kwDOD_-BVNoAKDFhNTIxNWE1MGM4ZDllOGEwYTFhNjhmYWZkYzE5MzA5YTRkMDMwZmM", - "C_kwDOD_-BVNoAKDRiZTQ4MTQ0MzgwYjBlNGEwNjQ4YjY4YWI4ZjFjYmQ3MWU4M2VhMzU", -] -TEST_SHA = "ad83e6340377904fa0295745b5314202b23d2f3f" - -# WRAPPED - -# example users, don't need to star the repo -USER_WHITELIST = [ - "torvalds", - "yyx990803", - "shadcn", - "sindresorhus", -] - -USER_BLACKLIST = ["kangmingtay", "ae7er", "stalukdar7", "piyush7833"] - -print("PROD", PROD) -print("API_VERSION", API_VERSION) -print() diff --git a/frontend/backend/src/data/__init__.py b/frontend/backend/src/data/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/data/github/__init__.py b/frontend/backend/src/data/github/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/data/github/auth/__init__.py b/frontend/backend/src/data/github/auth/__init__.py deleted file mode 100644 index 1a576063..00000000 --- a/frontend/backend/src/data/github/auth/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from src.data.github.auth.main import authenticate - -__all__ = ["authenticate"] diff --git a/frontend/backend/src/data/github/auth/main.py b/frontend/backend/src/data/github/auth/main.py deleted file mode 100644 index 175a476e..00000000 --- a/frontend/backend/src/data/github/auth/main.py +++ /dev/null @@ -1,57 +0,0 @@ -from datetime import datetime -from typing import Dict, Optional, Tuple - -import requests - -from src.constants import OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_REDIRECT_URI - -s = requests.session() - - -def get_unknown_user(access_token: str) -> Optional[str]: - """ - Accepts access_token and returns user_id of associated user - :param access_token: GitHub access token - :return: user_id or None if invalid access_token - """ - headers: Dict[str, str] = { - "Accept": "application/vnd.github.v3+json", - "Authorization": f"bearer {access_token}", - } - - r = s.get("https://api.github.com/user", params={}, headers=headers) - return r.json().get("login", None) - - -class OAuthError(Exception): - pass - - -async def authenticate(code: str) -> Tuple[str, str]: - """ - Takes a authentication code, verifies, and returns user_id/access_token - :param code: GitHub authentication code from OAuth process - :return: user_id, access_token of authenticated user - """ - start = datetime.now() - - params = { - "client_id": OAUTH_CLIENT_ID, - "client_secret": OAUTH_CLIENT_SECRET, - "code": code, - "redirect_uri": OAUTH_REDIRECT_URI, - } - - r = s.post("https://github.com/login/oauth/access_token", params=params) - - if r.status_code != 200: - raise OAuthError(f"OAuth Error: {str(r.status_code)}") - - access_token = r.text.split("&")[0].split("=")[1] - user_id = get_unknown_user(access_token) - - if user_id is None: - raise OAuthError("OAuth Error: Invalid user_id/access_token") - - print("OAuth SignUp", datetime.now() - start) - return user_id, access_token diff --git a/frontend/backend/src/data/github/extensions.json b/frontend/backend/src/data/github/extensions.json deleted file mode 100644 index 201c4173..00000000 --- a/frontend/backend/src/data/github/extensions.json +++ /dev/null @@ -1,3326 +0,0 @@ -{ - ".1": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".1in": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".1m": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".1x": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".2": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".3": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".3in": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".3m": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".3p": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".3pm": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".3qt": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".3x": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".4": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".4dm": { - "color": "#004289", - "name": "4D" - }, - ".4th": { - "color": "#341708", - "name": "Forth" - }, - ".5": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".6": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".6pl": { - "color": "#0000fb", - "name": "Raku" - }, - ".6pm": { - "color": "#0000fb", - "name": "Raku" - }, - ".7": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".8": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".8xk": { - "color": "#A0AA87", - "name": "TI Program" - }, - ".8xk.txt": { - "color": "#A0AA87", - "name": "TI Program" - }, - ".8xp": { - "color": "#A0AA87", - "name": "TI Program" - }, - ".8xp.txt": { - "color": "#A0AA87", - "name": "TI Program" - }, - ".9": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".E": { - "color": "#ccce35", - "name": "E" - }, - "._coffee": { - "color": "#244776", - "name": "CoffeeScript" - }, - "._js": { - "color": "#f1e05a", - "name": "JavaScript" - }, - "._ls": { - "color": "#499886", - "name": "LiveScript" - }, - ".a51": { - "color": "#6E4C13", - "name": "Assembly" - }, - ".abap": { - "color": "#E8274B", - "name": "ABAP" - }, - ".ada": { - "color": "#02f88c", - "name": "Ada" - }, - ".adb": { - "color": "#02f88c", - "name": "Ada" - }, - ".ado": { - "color": "#1a5f91", - "name": "Stata" - }, - ".adp": { - "color": "#e4cc98", - "name": "Tcl" - }, - ".ads": { - "color": "#02f88c", - "name": "Ada" - }, - ".agc": { - "color": "#0B3D91", - "name": "Apollo Guidance Computer" - }, - ".agda": { - "color": "#315665", - "name": "Agda" - }, - ".ahk": { - "color": "#6594b9", - "name": "AutoHotkey" - }, - ".ahkl": { - "color": "#6594b9", - "name": "AutoHotkey" - }, - ".aidl": { - "color": "#34EB6B", - "name": "AIDL" - }, - ".aj": { - "color": "#a957b0", - "name": "AspectJ" - }, - ".al": { - "color": "#0298c3", - "name": "Perl" - }, - ".als": { - "color": "#64C800", - "name": "Alloy" - }, - ".ampl": { - "color": "#E6EFBB", - "name": "AMPL" - }, - ".angelscript": { - "color": "#C7D7DC", - "name": "AngelScript" - }, - ".apib": { - "color": "#2ACCA8", - "name": "API Blueprint" - }, - ".apl": { - "color": "#5A8164", - "name": "APL" - }, - ".app.src": { - "color": "#B83998", - "name": "Erlang" - }, - ".applescript": { - "color": "#101F1F", - "name": "AppleScript" - }, - ".arc": { - "color": "#aa2afe", - "name": "Arc" - }, - ".as": { - "color": "#C7D7DC", - "name": "AngelScript" - }, - ".asax": { - "color": "#9400ff", - "name": "ASP.NET" - }, - ".asc": { - "color": "#B9D9FF", - "name": "AGS Script" - }, - ".ascx": { - "color": "#9400ff", - "name": "ASP.NET" - }, - ".asd": { - "color": "#3fb68b", - "name": "Common Lisp" - }, - ".asddls": { - "color": "#555e25", - "name": "ABAP CDS" - }, - ".ash": { - "color": "#B9D9FF", - "name": "AGS Script" - }, - ".ashx": { - "color": "#9400ff", - "name": "ASP.NET" - }, - ".asm": { - "color": "#005daa", - "name": "Motorola 68K Assembly" - }, - ".asmx": { - "color": "#9400ff", - "name": "ASP.NET" - }, - ".asp": { - "color": "#6a40fd", - "name": "Classic ASP" - }, - ".aspx": { - "color": "#9400ff", - "name": "ASP.NET" - }, - ".astro": { - "color": "#ff5a03", - "name": "Astro" - }, - ".asy": { - "color": "#ff0000", - "name": "Asymptote" - }, - ".au3": { - "color": "#1C3552", - "name": "AutoIt" - }, - ".aug": { - "color": "#9CC134", - "name": "Augeas" - }, - ".auk": { - "color": "#c30e9b", - "name": "Awk" - }, - ".aux": { - "color": "#3D6117", - "name": "TeX" - }, - ".aw": { - "color": "#4F5D95", - "name": "PHP" - }, - ".awk": { - "color": "#c30e9b", - "name": "Awk" - }, - ".axd": { - "color": "#9400ff", - "name": "ASP.NET" - }, - ".axi": { - "color": "#0aa0ff", - "name": "NetLinx" - }, - ".axi.erb": { - "color": "#747faa", - "name": "NetLinx+ERB" - }, - ".axs": { - "color": "#0aa0ff", - "name": "NetLinx" - }, - ".axs.erb": { - "color": "#747faa", - "name": "NetLinx+ERB" - }, - ".b": { - "color": "#2F2530", - "name": "Brainfuck" - }, - ".bal": { - "color": "#FF5000", - "name": "Ballerina" - }, - ".bas": { - "color": "#867db1", - "name": "VBA" - }, - ".bash": { - "color": "#89e051", - "name": "Shell" - }, - ".bat": { - "color": "#C1F12E", - "name": "Batchfile" - }, - ".bats": { - "color": "#89e051", - "name": "Shell" - }, - ".bb": { - "color": "#00FFAE", - "name": "BlitzBasic" - }, - ".bbx": { - "color": "#3D6117", - "name": "TeX" - }, - ".bdy": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".bf": { - "color": "#2F2530", - "name": "Brainfuck" - }, - ".bi": { - "color": "#867db1", - "name": "FreeBasic" - }, - ".bib": { - "color": "#778899", - "name": "BibTeX" - }, - ".bibtex": { - "color": "#778899", - "name": "BibTeX" - }, - ".bicep": { - "color": "#519aba", - "name": "Bicep" - }, - ".bison": { - "color": "#6A463F", - "name": "Bison" - }, - ".blade": { - "color": "#f7523f", - "name": "Blade" - }, - ".blade.php": { - "color": "#f7523f", - "name": "Blade" - }, - ".bmx": { - "color": "#cd6400", - "name": "BlitzMax" - }, - ".bones": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".boo": { - "color": "#d4bec1", - "name": "Boo" - }, - ".boot": { - "color": "#db5855", - "name": "Clojure" - }, - ".bpl": { - "color": "#c80fa0", - "name": "Boogie" - }, - ".brs": { - "color": "#662D91", - "name": "Brightscript" - }, - ".bsl": { - "color": "#814CCC", - "name": "1C Enterprise" - }, - ".bsv": { - "color": "#12223c", - "name": "Bluespec" - }, - ".builder": { - "color": "#701516", - "name": "Ruby" - }, - ".bzl": { - "color": "#76d275", - "name": "Starlark" - }, - ".c": { - "color": "#555555", - "name": "C" - }, - ".c++": { - "color": "#f34b7d", - "name": "C++" - }, - ".cake": { - "color": "#244776", - "name": "CoffeeScript" - }, - ".capnp": { - "color": "#c42727", - "name": "Cap'n Proto" - }, - ".cats": { - "color": "#555555", - "name": "C" - }, - ".cbx": { - "color": "#3D6117", - "name": "TeX" - }, - ".cc": { - "color": "#f34b7d", - "name": "C++" - }, - ".cdf": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".ceylon": { - "color": "#dfa535", - "name": "Ceylon" - }, - ".cfc": { - "color": "#ed2cd6", - "name": "ColdFusion CFC" - }, - ".cfm": { - "color": "#ed2cd6", - "name": "ColdFusion" - }, - ".cfml": { - "color": "#ed2cd6", - "name": "ColdFusion" - }, - ".cgi": { - "color": "#89e051", - "name": "Shell" - }, - ".cginc": { - "color": "#aace60", - "name": "HLSL" - }, - ".ch": { - "color": "#403a40", - "name": "xBase" - }, - ".chpl": { - "color": "#8dc63f", - "name": "Chapel" - }, - ".cirru": { - "color": "#ccccff", - "name": "Cirru" - }, - ".cjs": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".cjsx": { - "color": "#244776", - "name": "CoffeeScript" - }, - ".ck": { - "color": "#3f8000", - "name": "ChucK" - }, - ".cl": { - "color": "#ed2e2d", - "name": "OpenCL" - }, - ".cl2": { - "color": "#db5855", - "name": "Clojure" - }, - ".click": { - "color": "#E4E6F3", - "name": "Click" - }, - ".clj": { - "color": "#db5855", - "name": "Clojure" - }, - ".cljc": { - "color": "#db5855", - "name": "Clojure" - }, - ".cljs": { - "color": "#db5855", - "name": "Clojure" - }, - ".cljs.hl": { - "color": "#db5855", - "name": "Clojure" - }, - ".cljscm": { - "color": "#db5855", - "name": "Clojure" - }, - ".cljx": { - "color": "#db5855", - "name": "Clojure" - }, - ".clp": { - "color": "#00A300", - "name": "CLIPS" - }, - ".cls": { - "color": "#867db1", - "name": "VBA" - }, - ".clw": { - "color": "#db901e", - "name": "Clarion" - }, - ".cmake": { - "color": "#DA3434", - "name": "CMake" - }, - ".cmake.in": { - "color": "#DA3434", - "name": "CMake" - }, - ".cmd": { - "color": "#C1F12E", - "name": "Batchfile" - }, - ".cnc": { - "color": "#D08CF2", - "name": "G-code" - }, - ".cocci": { - "color": "#c94949", - "name": "SmPL" - }, - ".coffee": { - "color": "#244776", - "name": "CoffeeScript" - }, - ".coffee.md": { - "color": "#244776", - "name": "Literate CoffeeScript" - }, - ".command": { - "color": "#89e051", - "name": "Shell" - }, - ".coq": { - "color": "#d0b68c", - "name": "Coq" - }, - ".cp": { - "color": "#B0CE4E", - "name": "Component Pascal" - }, - ".cpp": { - "color": "#f34b7d", - "name": "C++" - }, - ".cps": { - "color": "#B0CE4E", - "name": "Component Pascal" - }, - ".cr": { - "color": "#000100", - "name": "Crystal" - }, - ".cs": { - "color": "#178600", - "name": "C#" - }, - ".csd": { - "color": "#1a1a1a", - "name": "Csound Document" - }, - ".cshtml": { - "color": "#512be4", - "name": "HTML+Razor" - }, - ".css": { - "color": "#563d7c", - "name": "CSS" - }, - ".csx": { - "color": "#178600", - "name": "C#" - }, - ".ctp": { - "color": "#4F5D95", - "name": "PHP" - }, - ".cu": { - "color": "#3A4E3A", - "name": "Cuda" - }, - ".cue": { - "color": "#5886E1", - "name": "CUE" - }, - ".cuh": { - "color": "#3A4E3A", - "name": "Cuda" - }, - ".cwl": { - "color": "#B5314C", - "name": "Common Workflow Language" - }, - ".cxx": { - "color": "#f34b7d", - "name": "C++" - }, - ".d": { - "color": "#427819", - "name": "Makefile" - }, - ".dart": { - "color": "#00B4AB", - "name": "Dart" - }, - ".dats": { - "color": "#1ac620", - "name": "ATS" - }, - ".db2": { - "color": "#e38c00", - "name": "SQLPL" - }, - ".dcl": { - "color": "#3F85AF", - "name": "Clean" - }, - ".ddl": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".decls": { - "color": "#00FFAE", - "name": "BlitzBasic" - }, - ".dfm": { - "color": "#E3F171", - "name": "Pascal" - }, - ".dfy": { - "color": "#FFEC25", - "name": "Dafny" - }, - ".dhall": { - "color": "#dfafff", - "name": "Dhall" - }, - ".di": { - "color": "#ba595e", - "name": "D" - }, - ".djs": { - "color": "#cca760", - "name": "Dogescript" - }, - ".dlm": { - "color": "#a3522f", - "name": "IDL" - }, - ".dm": { - "color": "#447265", - "name": "DM" - }, - ".do": { - "color": "#1a5f91", - "name": "Stata" - }, - ".dockerfile": { - "color": "#384d54", - "name": "Dockerfile" - }, - ".doh": { - "color": "#1a5f91", - "name": "Stata" - }, - ".dpr": { - "color": "#E3F171", - "name": "Pascal" - }, - ".druby": { - "color": "#c7a938", - "name": "Mirah" - }, - ".dsp": { - "color": "#c37240", - "name": "Faust" - }, - ".dtx": { - "color": "#3D6117", - "name": "TeX" - }, - ".duby": { - "color": "#c7a938", - "name": "Mirah" - }, - ".dwl": { - "color": "#003a52", - "name": "DataWeave" - }, - ".dyalog": { - "color": "#5A8164", - "name": "APL" - }, - ".dyl": { - "color": "#6c616e", - "name": "Dylan" - }, - ".dylan": { - "color": "#6c616e", - "name": "Dylan" - }, - ".e": { - "color": "#4d6977", - "name": "Eiffel" - }, - ".ebuild": { - "color": "#9400ff", - "name": "Gentoo Ebuild" - }, - ".ec": { - "color": "#913960", - "name": "eC" - }, - ".ecl": { - "color": "#001d9d", - "name": "ECLiPSe" - }, - ".eclass": { - "color": "#9400ff", - "name": "Gentoo Eclass" - }, - ".eclxml": { - "color": "#8a1267", - "name": "ECL" - }, - ".ecr": { - "color": "#2e1052", - "name": "HTML+ECR" - }, - ".ect": { - "color": "#a91e50", - "name": "EJS" - }, - ".eex": { - "color": "#6e4a7e", - "name": "HTML+EEX" - }, - ".eh": { - "color": "#913960", - "name": "eC" - }, - ".ejs": { - "color": "#a91e50", - "name": "EJS" - }, - ".el": { - "color": "#c065db", - "name": "Emacs Lisp" - }, - ".eliom": { - "color": "#3be133", - "name": "OCaml" - }, - ".eliomi": { - "color": "#3be133", - "name": "OCaml" - }, - ".elm": { - "color": "#60B5CC", - "name": "Elm" - }, - ".em": { - "color": "#FFF4F3", - "name": "EmberScript" - }, - ".emacs": { - "color": "#c065db", - "name": "Emacs Lisp" - }, - ".emacs.desktop": { - "color": "#c065db", - "name": "Emacs Lisp" - }, - ".emberscript": { - "color": "#FFF4F3", - "name": "EmberScript" - }, - ".env": { - "color": "#89e051", - "name": "Shell" - }, - ".eps": { - "color": "#da291c", - "name": "PostScript" - }, - ".epsi": { - "color": "#da291c", - "name": "PostScript" - }, - ".eq": { - "color": "#a78649", - "name": "EQ" - }, - ".erb": { - "color": "#701516", - "name": "HTML+ERB" - }, - ".erb.deface": { - "color": "#701516", - "name": "HTML+ERB" - }, - ".erl": { - "color": "#B83998", - "name": "Erlang" - }, - ".es": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".es6": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".escript": { - "color": "#B83998", - "name": "Erlang" - }, - ".ex": { - "color": "#6e4a7e", - "name": "Elixir" - }, - ".exs": { - "color": "#6e4a7e", - "name": "Elixir" - }, - ".eye": { - "color": "#701516", - "name": "Ruby" - }, - ".f": { - "color": "#4d41b1", - "name": "Fortran" - }, - ".f03": { - "color": "#4d41b1", - "name": "Fortran Free Form" - }, - ".f08": { - "color": "#4d41b1", - "name": "Fortran Free Form" - }, - ".f77": { - "color": "#4d41b1", - "name": "Fortran" - }, - ".f90": { - "color": "#4d41b1", - "name": "Fortran Free Form" - }, - ".f95": { - "color": "#4d41b1", - "name": "Fortran Free Form" - }, - ".factor": { - "color": "#636746", - "name": "Factor" - }, - ".fan": { - "color": "#14253c", - "name": "Fantom" - }, - ".fancypack": { - "color": "#7b9db4", - "name": "Fancy" - }, - ".fcgi": { - "color": "#89e051", - "name": "Shell" - }, - ".feature": { - "color": "#5B2063", - "name": "Gherkin" - }, - ".fish": { - "color": "#4aae47", - "name": "fish" - }, - ".flex": { - "color": "#DBCA00", - "name": "JFlex" - }, - ".flux": { - "color": "#88ccff", - "name": "FLUX" - }, - ".fnc": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".fnl": { - "color": "#fff3d7", - "name": "Fennel" - }, - ".for": { - "color": "#4d41b1", - "name": "Fortran" - }, - ".forth": { - "color": "#341708", - "name": "Forth" - }, - ".fp": { - "color": "#5686a5", - "name": "GLSL" - }, - ".fpp": { - "color": "#4d41b1", - "name": "Fortran" - }, - ".fr": { - "color": "#00cafe", - "name": "Frege" - }, - ".frag": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".frg": { - "color": "#5686a5", - "name": "GLSL" - }, - ".frm": { - "color": "#867db1", - "name": "VBA" - }, - ".frt": { - "color": "#341708", - "name": "Forth" - }, - ".frx": { - "color": "#867db1", - "name": "VBA" - }, - ".fs": { - "color": "#5686a5", - "name": "GLSL" - }, - ".fsh": { - "color": "#5686a5", - "name": "GLSL" - }, - ".fshader": { - "color": "#5686a5", - "name": "GLSL" - }, - ".fsi": { - "color": "#b845fc", - "name": "F#" - }, - ".fst": { - "color": "#572e30", - "name": "F*" - }, - ".fsx": { - "color": "#b845fc", - "name": "F#" - }, - ".fth": { - "color": "#341708", - "name": "Forth" - }, - ".ftl": { - "color": "#0050b2", - "name": "FreeMarker" - }, - ".fun": { - "color": "#dc566d", - "name": "Standard ML" - }, - ".fut": { - "color": "#5f021f", - "name": "Futhark" - }, - ".fx": { - "color": "#aace60", - "name": "HLSL" - }, - ".fxh": { - "color": "#aace60", - "name": "HLSL" - }, - ".fy": { - "color": "#7b9db4", - "name": "Fancy" - }, - ".g": { - "color": "#0000cc", - "name": "GAP" - }, - ".g4": { - "color": "#9DC3FF", - "name": "ANTLR" - }, - ".gaml": { - "color": "#FFC766", - "name": "GAML" - }, - ".gap": { - "color": "#0000cc", - "name": "GAP" - }, - ".gawk": { - "color": "#c30e9b", - "name": "Awk" - }, - ".gco": { - "color": "#D08CF2", - "name": "G-code" - }, - ".gcode": { - "color": "#D08CF2", - "name": "G-code" - }, - ".gd": { - "color": "#355570", - "name": "GDScript" - }, - ".gemspec": { - "color": "#701516", - "name": "Ruby" - }, - ".geo": { - "color": "#5686a5", - "name": "GLSL" - }, - ".geom": { - "color": "#5686a5", - "name": "GLSL" - }, - ".gf": { - "color": "#ff0000", - "name": "Grammatical Framework" - }, - ".gi": { - "color": "#0000cc", - "name": "GAP" - }, - ".glf": { - "color": "#c1ac7f", - "name": "Glyph" - }, - ".glsl": { - "color": "#5686a5", - "name": "GLSL" - }, - ".glslf": { - "color": "#5686a5", - "name": "GLSL" - }, - ".glslv": { - "color": "#5686a5", - "name": "GLSL" - }, - ".gml": { - "color": "#71b417", - "name": "Game Maker Language" - }, - ".gms": { - "color": "#f49a22", - "name": "GAMS" - }, - ".gnu": { - "color": "#f0a9f0", - "name": "Gnuplot" - }, - ".gnuplot": { - "color": "#f0a9f0", - "name": "Gnuplot" - }, - ".go": { - "color": "#00ADD8", - "name": "Go" - }, - ".god": { - "color": "#701516", - "name": "Ruby" - }, - ".golo": { - "color": "#88562A", - "name": "Golo" - }, - ".gp": { - "color": "#f0a9f0", - "name": "Gnuplot" - }, - ".grace": { - "color": "#615f8b", - "name": "Grace" - }, - ".groovy": { - "color": "#4298b8", - "name": "Groovy" - }, - ".grt": { - "color": "#4298b8", - "name": "Groovy" - }, - ".gs": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".gshader": { - "color": "#5686a5", - "name": "GLSL" - }, - ".gsp": { - "color": "#4298b8", - "name": "Groovy Server Pages" - }, - ".gst": { - "color": "#82937f", - "name": "Gosu" - }, - ".gsx": { - "color": "#82937f", - "name": "Gosu" - }, - ".gtpl": { - "color": "#4298b8", - "name": "Groovy" - }, - ".gvy": { - "color": "#4298b8", - "name": "Groovy" - }, - ".gyp": { - "color": "#3572A5", - "name": "Python" - }, - ".gypi": { - "color": "#3572A5", - "name": "Python" - }, - ".h": { - "color": "#438eff", - "name": "Objective-C" - }, - ".h++": { - "color": "#f34b7d", - "name": "C++" - }, - ".hack": { - "color": "#878787", - "name": "Hack" - }, - ".haml": { - "color": "#ece2a9", - "name": "Haml" - }, - ".haml.deface": { - "color": "#ece2a9", - "name": "Haml" - }, - ".handlebars": { - "color": "#f7931e", - "name": "Handlebars" - }, - ".hats": { - "color": "#1ac620", - "name": "ATS" - }, - ".hb": { - "color": "#0e60e3", - "name": "Harbour" - }, - ".hbs": { - "color": "#f7931e", - "name": "Handlebars" - }, - ".hc": { - "color": "#ffefaf", - "name": "HolyC" - }, - ".hh": { - "color": "#878787", - "name": "Hack" - }, - ".hhi": { - "color": "#878787", - "name": "Hack" - }, - ".hic": { - "color": "#db5855", - "name": "Clojure" - }, - ".hlsl": { - "color": "#aace60", - "name": "HLSL" - }, - ".hlsli": { - "color": "#aace60", - "name": "HLSL" - }, - ".hpp": { - "color": "#f34b7d", - "name": "C++" - }, - ".hqf": { - "color": "#3F3F3F", - "name": "SQF" - }, - ".hql": { - "color": "#dce200", - "name": "HiveQL" - }, - ".hrl": { - "color": "#B83998", - "name": "Erlang" - }, - ".hs": { - "color": "#5e5086", - "name": "Haskell" - }, - ".hs-boot": { - "color": "#5e5086", - "name": "Haskell" - }, - ".hsc": { - "color": "#5e5086", - "name": "Haskell" - }, - ".hta": { - "color": "#e34c26", - "name": "HTML" - }, - ".htm": { - "color": "#e34c26", - "name": "HTML" - }, - ".html": { - "color": "#e34c26", - "name": "HTML" - }, - ".html.hl": { - "color": "#e34c26", - "name": "HTML" - }, - ".html.leex": { - "color": "#6e4a7e", - "name": "HTML+EEX" - }, - ".hx": { - "color": "#df7900", - "name": "Haxe" - }, - ".hxsl": { - "color": "#df7900", - "name": "Haxe" - }, - ".hxx": { - "color": "#f34b7d", - "name": "C++" - }, - ".hy": { - "color": "#7790B2", - "name": "Hy" - }, - ".i": { - "color": "#005daa", - "name": "Motorola 68K Assembly" - }, - ".i3": { - "color": "#223388", - "name": "Modula-3" - }, - ".ice": { - "color": "#003fa2", - "name": "Slice" - }, - ".iced": { - "color": "#244776", - "name": "CoffeeScript" - }, - ".icl": { - "color": "#3F85AF", - "name": "Clean" - }, - ".idc": { - "color": "#555555", - "name": "C" - }, - ".idr": { - "color": "#b30000", - "name": "Idris" - }, - ".ig": { - "color": "#223388", - "name": "Modula-3" - }, - ".ihlp": { - "color": "#1a5f91", - "name": "Stata" - }, - ".ijm": { - "color": "#99AAFF", - "name": "ImageJ Macro" - }, - ".ijs": { - "color": "#9EEDFF", - "name": "J" - }, - ".ik": { - "color": "#078193", - "name": "Ioke" - }, - ".ily": { - "color": "#9ccc7c", - "name": "LilyPond" - }, - ".inc": { - "color": "#f69e1d", - "name": "SourcePawn" - }, - ".inl": { - "color": "#f34b7d", - "name": "C++" - }, - ".ino": { - "color": "#f34b7d", - "name": "C++" - }, - ".ins": { - "color": "#3D6117", - "name": "TeX" - }, - ".intr": { - "color": "#6c616e", - "name": "Dylan" - }, - ".io": { - "color": "#a9188d", - "name": "Io" - }, - ".iol": { - "color": "#843179", - "name": "Jolie" - }, - ".ipf": { - "color": "#0000cc", - "name": "IGOR Pro" - }, - ".ipp": { - "color": "#f34b7d", - "name": "C++" - }, - ".ipynb": { - "color": "#DA5B0B", - "name": "Jupyter Notebook" - }, - ".isl": { - "color": "#264b99", - "name": "Inno Setup" - }, - ".iss": { - "color": "#264b99", - "name": "Inno Setup" - }, - ".j": { - "color": "#ff0c5a", - "name": "Objective-J" - }, - ".j2": { - "color": "#a52a22", - "name": "Jinja" - }, - ".jade": { - "color": "#a86454", - "name": "Pug" - }, - ".jake": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".jav": { - "color": "#b07219", - "name": "Java" - }, - ".java": { - "color": "#b07219", - "name": "Java" - }, - ".javascript": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".jbuilder": { - "color": "#701516", - "name": "Ruby" - }, - ".jflex": { - "color": "#DBCA00", - "name": "JFlex" - }, - ".jinja": { - "color": "#a52a22", - "name": "Jinja" - }, - ".jinja2": { - "color": "#a52a22", - "name": "Jinja" - }, - ".jison": { - "color": "#56b3cb", - "name": "Jison" - }, - ".jisonlex": { - "color": "#56b3cb", - "name": "Jison Lex" - }, - ".jl": { - "color": "#a270ba", - "name": "Julia" - }, - ".jq": { - "color": "#c7254e", - "name": "jq" - }, - ".js": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".js.erb": { - "color": "#f1e05a", - "name": "JavaScript+ERB" - }, - ".jsb": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".jscad": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".jsfl": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".jsm": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".jsonnet": { - "color": "#0064bd", - "name": "Jsonnet" - }, - ".jsp": { - "color": "#2A6277", - "name": "Java Server Pages" - }, - ".jss": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".jst": { - "color": "#a91e50", - "name": "EJS" - }, - ".jsx": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".kak": { - "color": "#6f8042", - "name": "KakouneScript" - }, - ".kid": { - "color": "#951531", - "name": "Genshi" - }, - ".kojo": { - "color": "#c22d40", - "name": "Scala" - }, - ".krl": { - "color": "#28430A", - "name": "KRL" - }, - ".ksh": { - "color": "#89e051", - "name": "Shell" - }, - ".ksy": { - "color": "#773b37", - "name": "Kaitai Struct" - }, - ".kt": { - "color": "#A97BFF", - "name": "Kotlin" - }, - ".ktm": { - "color": "#A97BFF", - "name": "Kotlin" - }, - ".kts": { - "color": "#A97BFF", - "name": "Kotlin" - }, - ".l": { - "color": "#ecdebe", - "name": "Roff" - }, - ".lagda": { - "color": "#315665", - "name": "Literate Agda" - }, - ".las": { - "color": "#999999", - "name": "Lasso" - }, - ".lasso": { - "color": "#999999", - "name": "Lasso" - }, - ".lasso8": { - "color": "#999999", - "name": "Lasso" - }, - ".lasso9": { - "color": "#999999", - "name": "Lasso" - }, - ".latte": { - "color": "#f2a542", - "name": "Latte" - }, - ".lbx": { - "color": "#3D6117", - "name": "TeX" - }, - ".less": { - "color": "#1d365d", - "name": "Less" - }, - ".lex": { - "color": "#DBCA00", - "name": "Lex" - }, - ".lfe": { - "color": "#4C3023", - "name": "LFE" - }, - ".lgt": { - "color": "#295b9a", - "name": "Logtalk" - }, - ".lhs": { - "color": "#5e5086", - "name": "Literate Haskell" - }, - ".libsonnet": { - "color": "#0064bd", - "name": "Jsonnet" - }, - ".lid": { - "color": "#6c616e", - "name": "Dylan" - }, - ".lidr": { - "color": "#b30000", - "name": "Idris" - }, - ".linq": { - "color": "#178600", - "name": "C#" - }, - ".liquid": { - "color": "#67b8de", - "name": "Liquid" - }, - ".lisp": { - "color": "#87AED7", - "name": "NewLisp" - }, - ".litcoffee": { - "color": "#244776", - "name": "Literate CoffeeScript" - }, - ".ll": { - "color": "#185619", - "name": "LLVM" - }, - ".lmi": { - "color": "#3572A5", - "name": "Python" - }, - ".logtalk": { - "color": "#295b9a", - "name": "Logtalk" - }, - ".lol": { - "color": "#cc9900", - "name": "LOLCODE" - }, - ".lookml": { - "color": "#652B81", - "name": "LookML" - }, - ".lpr": { - "color": "#E3F171", - "name": "Pascal" - }, - ".ls": { - "color": "#499886", - "name": "LiveScript" - }, - ".lsl": { - "color": "#3d9970", - "name": "LSL" - }, - ".lslp": { - "color": "#3d9970", - "name": "LSL" - }, - ".lsp": { - "color": "#87AED7", - "name": "NewLisp" - }, - ".ltx": { - "color": "#3D6117", - "name": "TeX" - }, - ".lua": { - "color": "#000080", - "name": "Lua" - }, - ".lvlib": { - "color": "#fede06", - "name": "LabVIEW" - }, - ".lvproj": { - "color": "#fede06", - "name": "LabVIEW" - }, - ".ly": { - "color": "#9ccc7c", - "name": "LilyPond" - }, - ".m": { - "color": "#438eff", - "name": "Objective-C" - }, - ".m2": { - "color": "#d8ffff", - "name": "Macaulay2" - }, - ".m3": { - "color": "#223388", - "name": "Modula-3" - }, - ".ma": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".mak": { - "color": "#427819", - "name": "Makefile" - }, - ".make": { - "color": "#427819", - "name": "Makefile" - }, - ".makefile": { - "color": "#427819", - "name": "Makefile" - }, - ".mako": { - "color": "#7e858d", - "name": "Mako" - }, - ".man": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".mao": { - "color": "#7e858d", - "name": "Mako" - }, - ".marko": { - "color": "#42bff2", - "name": "Marko" - }, - ".mask": { - "color": "#f97732", - "name": "Mask" - }, - ".mata": { - "color": "#1a5f91", - "name": "Stata" - }, - ".matah": { - "color": "#1a5f91", - "name": "Stata" - }, - ".mathematica": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".matlab": { - "color": "#e16737", - "name": "MATLAB" - }, - ".mawk": { - "color": "#c30e9b", - "name": "Awk" - }, - ".maxhelp": { - "color": "#c4a79c", - "name": "Max" - }, - ".maxpat": { - "color": "#c4a79c", - "name": "Max" - }, - ".maxproj": { - "color": "#c4a79c", - "name": "Max" - }, - ".mcfunction": { - "color": "#E22837", - "name": "mcfunction" - }, - ".mcr": { - "color": "#00a6a6", - "name": "MAXScript" - }, - ".mdoc": { - "color": "#ecdebe", - "name": "Roff Manpage" - }, - ".me": { - "color": "#ecdebe", - "name": "Roff" - }, - ".metal": { - "color": "#8f14e9", - "name": "Metal" - }, - ".mg": { - "color": "#223388", - "name": "Modula-3" - }, - ".mirah": { - "color": "#c7a938", - "name": "Mirah" - }, - ".mjs": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".mk": { - "color": "#427819", - "name": "Makefile" - }, - ".mkfile": { - "color": "#427819", - "name": "Makefile" - }, - ".mkii": { - "color": "#3D6117", - "name": "TeX" - }, - ".mkiv": { - "color": "#3D6117", - "name": "TeX" - }, - ".mkvi": { - "color": "#3D6117", - "name": "TeX" - }, - ".ml": { - "color": "#dc566d", - "name": "Standard ML" - }, - ".ml4": { - "color": "#3be133", - "name": "OCaml" - }, - ".mli": { - "color": "#3be133", - "name": "OCaml" - }, - ".mlir": { - "color": "#5EC8DB", - "name": "MLIR" - }, - ".mll": { - "color": "#3be133", - "name": "OCaml" - }, - ".mly": { - "color": "#3be133", - "name": "OCaml" - }, - ".mm": { - "color": "#6866fb", - "name": "Objective-C++" - }, - ".mo": { - "color": "#de1d31", - "name": "Modelica" - }, - ".mod": { - "color": "#10253f", - "name": "Modula-2" - }, - ".model.lkml": { - "color": "#652B81", - "name": "LookML" - }, - ".moo": { - "color": "#ff2b2b", - "name": "Mercury" - }, - ".moon": { - "color": "#ff4585", - "name": "MoonScript" - }, - ".mq4": { - "color": "#62A8D6", - "name": "MQL4" - }, - ".mq5": { - "color": "#4A76B8", - "name": "MQL5" - }, - ".mqh": { - "color": "#4A76B8", - "name": "MQL5" - }, - ".mrc": { - "color": "#3d57c3", - "name": "mIRC Script" - }, - ".ms": { - "color": "#ecdebe", - "name": "Roff" - }, - ".mspec": { - "color": "#701516", - "name": "Ruby" - }, - ".mt": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".mtml": { - "color": "#b7e1f4", - "name": "MTML" - }, - ".mu": { - "color": "#244963", - "name": "mupad" - }, - ".mud": { - "color": "#dc75e5", - "name": "ZIL" - }, - ".mustache": { - "color": "#724b3b", - "name": "Mustache" - }, - ".mxt": { - "color": "#c4a79c", - "name": "Max" - }, - ".n": { - "color": "#ecdebe", - "name": "Roff" - }, - ".nasm": { - "color": "#6E4C13", - "name": "Assembly" - }, - ".nawk": { - "color": "#c30e9b", - "name": "Awk" - }, - ".nb": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".nbp": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".nc": { - "color": "#94B0C7", - "name": "nesC" - }, - ".ncl": { - "color": "#28431f", - "name": "NCL" - }, - ".ne": { - "color": "#990000", - "name": "Nearley" - }, - ".nearley": { - "color": "#990000", - "name": "Nearley" - }, - ".nf": { - "color": "#3ac486", - "name": "Nextflow" - }, - ".nim": { - "color": "#ffc200", - "name": "Nim" - }, - ".nim.cfg": { - "color": "#ffc200", - "name": "Nim" - }, - ".nimble": { - "color": "#ffc200", - "name": "Nim" - }, - ".nimrod": { - "color": "#ffc200", - "name": "Nim" - }, - ".nims": { - "color": "#ffc200", - "name": "Nim" - }, - ".nit": { - "color": "#009917", - "name": "Nit" - }, - ".nix": { - "color": "#7e7eff", - "name": "Nix" - }, - ".njk": { - "color": "#3d8137", - "name": "Nunjucks" - }, - ".njs": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".nl": { - "color": "#87AED7", - "name": "NewLisp" - }, - ".nlogo": { - "color": "#ff6375", - "name": "NetLogo" - }, - ".nqp": { - "color": "#0000fb", - "name": "Raku" - }, - ".nr": { - "color": "#ecdebe", - "name": "Roff" - }, - ".nse": { - "color": "#000080", - "name": "Lua" - }, - ".nss": { - "color": "#111522", - "name": "NWScript" - }, - ".nu": { - "color": "#c9df40", - "name": "Nu" - }, - ".numpy": { - "color": "#9C8AF9", - "name": "NumPy" - }, - ".numpyw": { - "color": "#9C8AF9", - "name": "NumPy" - }, - ".numsc": { - "color": "#9C8AF9", - "name": "NumPy" - }, - ".nut": { - "color": "#800000", - "name": "Squirrel" - }, - ".ny": { - "color": "#3fb68b", - "name": "Common Lisp" - }, - ".odin": { - "color": "#60AFFE", - "name": "Odin" - }, - ".ol": { - "color": "#843179", - "name": "Jolie" - }, - ".omgrofl": { - "color": "#cabbff", - "name": "Omgrofl" - }, - ".ooc": { - "color": "#b0b77e", - "name": "ooc" - }, - ".opal": { - "color": "#f7ede0", - "name": "Opal" - }, - ".opencl": { - "color": "#ed2e2d", - "name": "OpenCL" - }, - ".orc": { - "color": "#1a1a1a", - "name": "Csound" - }, - ".os": { - "color": "#814CCC", - "name": "1C Enterprise" - }, - ".oxygene": { - "color": "#cdd0e3", - "name": "Oxygene" - }, - ".oz": { - "color": "#fab738", - "name": "Oz" - }, - ".p": { - "color": "#5ce600", - "name": "OpenEdge ABL" - }, - ".p4": { - "color": "#7055b5", - "name": "P4" - }, - ".p6": { - "color": "#0000fb", - "name": "Raku" - }, - ".p6l": { - "color": "#0000fb", - "name": "Raku" - }, - ".p6m": { - "color": "#0000fb", - "name": "Raku" - }, - ".p8": { - "color": "#000080", - "name": "Lua" - }, - ".pac": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".pan": { - "color": "#cc0000", - "name": "Pan" - }, - ".parrot": { - "color": "#f3ca0a", - "name": "Parrot" - }, - ".pas": { - "color": "#E3F171", - "name": "Pascal" - }, - ".pascal": { - "color": "#E3F171", - "name": "Pascal" - }, - ".pat": { - "color": "#c4a79c", - "name": "Max" - }, - ".pb": { - "color": "#5a6986", - "name": "PureBasic" - }, - ".pbi": { - "color": "#5a6986", - "name": "PureBasic" - }, - ".pbt": { - "color": "#8f0f8d", - "name": "PowerBuilder" - }, - ".pck": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".pcss": { - "color": "#dc3a0c", - "name": "PostCSS" - }, - ".pd_lua": { - "color": "#000080", - "name": "Lua" - }, - ".pde": { - "color": "#0096D8", - "name": "Processing" - }, - ".pegjs": { - "color": "#234d6b", - "name": "PEG.js" - }, - ".pep": { - "color": "#C76F5B", - "name": "Pep8" - }, - ".perl": { - "color": "#0298c3", - "name": "Perl" - }, - ".pfa": { - "color": "#da291c", - "name": "PostScript" - }, - ".pgsql": { - "color": "#336790", - "name": "PLpgSQL" - }, - ".ph": { - "color": "#0298c3", - "name": "Perl" - }, - ".php": { - "color": "#4F5D95", - "name": "PHP" - }, - ".php3": { - "color": "#4F5D95", - "name": "PHP" - }, - ".php4": { - "color": "#4F5D95", - "name": "PHP" - }, - ".php5": { - "color": "#4F5D95", - "name": "PHP" - }, - ".phps": { - "color": "#4F5D95", - "name": "PHP" - }, - ".phpt": { - "color": "#4F5D95", - "name": "PHP" - }, - ".phtml": { - "color": "#4f5d95", - "name": "HTML+PHP" - }, - ".pig": { - "color": "#fcd7de", - "name": "PigLatin" - }, - ".pike": { - "color": "#005390", - "name": "Pike" - }, - ".pkb": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".pks": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".pl": { - "color": "#0000fb", - "name": "Raku" - }, - ".pl6": { - "color": "#0000fb", - "name": "Raku" - }, - ".plb": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".plot": { - "color": "#f0a9f0", - "name": "Gnuplot" - }, - ".pls": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".plsql": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".plt": { - "color": "#f0a9f0", - "name": "Gnuplot" - }, - ".pluginspec": { - "color": "#701516", - "name": "Ruby" - }, - ".plx": { - "color": "#0298c3", - "name": "Perl" - }, - ".pm": { - "color": "#0000fb", - "name": "Raku" - }, - ".pm6": { - "color": "#0000fb", - "name": "Raku" - }, - ".pmod": { - "color": "#005390", - "name": "Pike" - }, - ".podsl": { - "color": "#3fb68b", - "name": "Common Lisp" - }, - ".podspec": { - "color": "#701516", - "name": "Ruby" - }, - ".pogo": { - "color": "#d80074", - "name": "PogoScript" - }, - ".postcss": { - "color": "#dc3a0c", - "name": "PostCSS" - }, - ".pov": { - "color": "#6bac65", - "name": "POV-Ray SDL" - }, - ".pp": { - "color": "#302B6D", - "name": "Puppet" - }, - ".pprx": { - "color": "#d90e09", - "name": "REXX" - }, - ".prawn": { - "color": "#701516", - "name": "Ruby" - }, - ".prc": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".prg": { - "color": "#403a40", - "name": "xBase" - }, - ".pro": { - "color": "#74283c", - "name": "Prolog" - }, - ".prolog": { - "color": "#74283c", - "name": "Prolog" - }, - ".prw": { - "color": "#403a40", - "name": "xBase" - }, - ".ps": { - "color": "#da291c", - "name": "PostScript" - }, - ".ps1": { - "color": "#012456", - "name": "PowerShell" - }, - ".psc": { - "color": "#6600cc", - "name": "Papyrus" - }, - ".psd1": { - "color": "#012456", - "name": "PowerShell" - }, - ".psgi": { - "color": "#0298c3", - "name": "Perl" - }, - ".psm1": { - "color": "#012456", - "name": "PowerShell" - }, - ".pug": { - "color": "#a86454", - "name": "Pug" - }, - ".purs": { - "color": "#1D222D", - "name": "PureScript" - }, - ".pwn": { - "color": "#dbb284", - "name": "Pawn" - }, - ".pxd": { - "color": "#fedf5b", - "name": "Cython" - }, - ".pxi": { - "color": "#fedf5b", - "name": "Cython" - }, - ".py": { - "color": "#3572A5", - "name": "Python" - }, - ".py3": { - "color": "#3572A5", - "name": "Python" - }, - ".pyde": { - "color": "#3572A5", - "name": "Python" - }, - ".pyi": { - "color": "#3572A5", - "name": "Python" - }, - ".pyp": { - "color": "#3572A5", - "name": "Python" - }, - ".pyt": { - "color": "#3572A5", - "name": "Python" - }, - ".pyw": { - "color": "#3572A5", - "name": "Python" - }, - ".pyx": { - "color": "#fedf5b", - "name": "Cython" - }, - ".q": { - "color": "#0040cd", - "name": "q" - }, - ".qasm": { - "color": "#AA70FF", - "name": "OpenQASM" - }, - ".qbs": { - "color": "#44a51c", - "name": "QML" - }, - ".ql": { - "color": "#140f46", - "name": "CodeQL" - }, - ".qll": { - "color": "#140f46", - "name": "CodeQL" - }, - ".qml": { - "color": "#44a51c", - "name": "QML" - }, - ".qs": { - "color": "#00b841", - "name": "Qt Script" - }, - ".r": { - "color": "#358a5b", - "name": "Rebol" - }, - ".r2": { - "color": "#358a5b", - "name": "Rebol" - }, - ".r3": { - "color": "#358a5b", - "name": "Rebol" - }, - ".rabl": { - "color": "#701516", - "name": "Ruby" - }, - ".rake": { - "color": "#701516", - "name": "Ruby" - }, - ".raku": { - "color": "#0000fb", - "name": "Raku" - }, - ".rakumod": { - "color": "#0000fb", - "name": "Raku" - }, - ".raml": { - "color": "#77d9fb", - "name": "RAML" - }, - ".razor": { - "color": "#512be4", - "name": "HTML+Razor" - }, - ".rb": { - "color": "#701516", - "name": "Ruby" - }, - ".rbi": { - "color": "#701516", - "name": "Ruby" - }, - ".rbuild": { - "color": "#701516", - "name": "Ruby" - }, - ".rbw": { - "color": "#701516", - "name": "Ruby" - }, - ".rbx": { - "color": "#701516", - "name": "Ruby" - }, - ".rbxs": { - "color": "#000080", - "name": "Lua" - }, - ".rd": { - "color": "#198CE7", - "name": "R" - }, - ".re": { - "color": "#ff5847", - "name": "Reason" - }, - ".reb": { - "color": "#358a5b", - "name": "Rebol" - }, - ".rebol": { - "color": "#358a5b", - "name": "Rebol" - }, - ".red": { - "color": "#f50000", - "name": "Red" - }, - ".reds": { - "color": "#f50000", - "name": "Red" - }, - ".rego": { - "color": "#7d9199", - "name": "Open Policy Agent" - }, - ".rei": { - "color": "#ff5847", - "name": "Reason" - }, - ".res": { - "color": "#ed5051", - "name": "ReScript" - }, - ".rex": { - "color": "#d90e09", - "name": "REXX" - }, - ".rexx": { - "color": "#d90e09", - "name": "REXX" - }, - ".rg": { - "color": "#cc0088", - "name": "Rouge" - }, - ".rhtml": { - "color": "#701516", - "name": "HTML+ERB" - }, - ".ring": { - "color": "#2D54CB", - "name": "Ring" - }, - ".riot": { - "color": "#A71E49", - "name": "Riot" - }, - ".rkt": { - "color": "#3c5caa", - "name": "Racket" - }, - ".rktd": { - "color": "#3c5caa", - "name": "Racket" - }, - ".rktl": { - "color": "#3c5caa", - "name": "Racket" - }, - ".rl": { - "color": "#9d5200", - "name": "Ragel" - }, - ".rnh": { - "color": "#665a4e", - "name": "RUNOFF" - }, - ".rno": { - "color": "#ecdebe", - "name": "Roff" - }, - ".robot": { - "color": "#00c0b5", - "name": "RobotFramework" - }, - ".rockspec": { - "color": "#000080", - "name": "Lua" - }, - ".roff": { - "color": "#ecdebe", - "name": "Roff" - }, - ".rpy": { - "color": "#ff7f7f", - "name": "Ren'Py" - }, - ".rs": { - "color": "#dea584", - "name": "Rust" - }, - ".rs.in": { - "color": "#dea584", - "name": "Rust" - }, - ".rsc": { - "color": "#fffaa0", - "name": "Rascal" - }, - ".rsx": { - "color": "#198CE7", - "name": "R" - }, - ".ru": { - "color": "#701516", - "name": "Ruby" - }, - ".ruby": { - "color": "#701516", - "name": "Ruby" - }, - ".s": { - "color": "#005daa", - "name": "Motorola 68K Assembly" - }, - ".sas": { - "color": "#B34936", - "name": "SAS" - }, - ".sass": { - "color": "#a53b70", - "name": "Sass" - }, - ".sats": { - "color": "#1ac620", - "name": "ATS" - }, - ".sbt": { - "color": "#c22d40", - "name": "Scala" - }, - ".sc": { - "color": "#46390b", - "name": "SuperCollider" - }, - ".scad": { - "color": "#e5cd45", - "name": "OpenSCAD" - }, - ".scala": { - "color": "#c22d40", - "name": "Scala" - }, - ".scaml": { - "color": "#bd181a", - "name": "Scaml" - }, - ".scd": { - "color": "#46390b", - "name": "SuperCollider" - }, - ".sce": { - "color": "#ca0f21", - "name": "Scilab" - }, - ".sch": { - "color": "#1e4aec", - "name": "Scheme" - }, - ".sci": { - "color": "#ca0f21", - "name": "Scilab" - }, - ".scm": { - "color": "#1e4aec", - "name": "Scheme" - }, - ".sco": { - "color": "#1a1a1a", - "name": "Csound Score" - }, - ".scpt": { - "color": "#101F1F", - "name": "AppleScript" - }, - ".scrbl": { - "color": "#3c5caa", - "name": "Racket" - }, - ".scss": { - "color": "#c6538c", - "name": "SCSS" - }, - ".sed": { - "color": "#64b970", - "name": "sed" - }, - ".self": { - "color": "#0579aa", - "name": "Self" - }, - ".sexp": { - "color": "#3fb68b", - "name": "Common Lisp" - }, - ".sh": { - "color": "#89e051", - "name": "Shell" - }, - ".sh.in": { - "color": "#89e051", - "name": "Shell" - }, - ".shader": { - "color": "#222c37", - "name": "ShaderLab" - }, - ".shen": { - "color": "#120F14", - "name": "Shen" - }, - ".sig": { - "color": "#dc566d", - "name": "Standard ML" - }, - ".sj": { - "color": "#ff0c5a", - "name": "Objective-J" - }, - ".sjs": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".sl": { - "color": "#007eff", - "name": "Slash" - }, - ".sld": { - "color": "#1e4aec", - "name": "Scheme" - }, - ".slim": { - "color": "#2b2b2b", - "name": "Slim" - }, - ".sls": { - "color": "#1e4aec", - "name": "Scheme" - }, - ".sma": { - "color": "#dbb284", - "name": "Pawn" - }, - ".smk": { - "color": "#3572A5", - "name": "Python" - }, - ".sml": { - "color": "#dc566d", - "name": "Standard ML" - }, - ".snip": { - "color": "#199f4b", - "name": "Vim Snippet" - }, - ".snippet": { - "color": "#199f4b", - "name": "Vim Snippet" - }, - ".snippets": { - "color": "#199f4b", - "name": "Vim Snippet" - }, - ".sol": { - "color": "#AA6746", - "name": "Solidity" - }, - ".soy": { - "color": "#0d948f", - "name": "Closure Templates" - }, - ".sp": { - "color": "#f69e1d", - "name": "SourcePawn" - }, - ".spc": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".spec": { - "color": "#701516", - "name": "Ruby" - }, - ".spin": { - "color": "#7fa2a7", - "name": "Propeller Spin" - }, - ".sps": { - "color": "#1e4aec", - "name": "Scheme" - }, - ".sqf": { - "color": "#3F3F3F", - "name": "SQF" - }, - ".sql": { - "color": "#e38c00", - "name": "TSQL" - }, - ".sra": { - "color": "#8f0f8d", - "name": "PowerBuilder" - }, - ".srt": { - "color": "#348a34", - "name": "SRecode Template" - }, - ".sru": { - "color": "#8f0f8d", - "name": "PowerBuilder" - }, - ".srw": { - "color": "#8f0f8d", - "name": "PowerBuilder" - }, - ".ss": { - "color": "#1e4aec", - "name": "Scheme" - }, - ".ssjs": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".sss": { - "color": "#2fcc9f", - "name": "SugarSS" - }, - ".st": { - "color": "#3fb34f", - "name": "StringTemplate" - }, - ".stan": { - "color": "#b2011d", - "name": "Stan" - }, - ".sthlp": { - "color": "#1a5f91", - "name": "Stata" - }, - ".story": { - "color": "#5B2063", - "name": "Gherkin" - }, - ".sty": { - "color": "#3D6117", - "name": "TeX" - }, - ".styl": { - "color": "#ff6347", - "name": "Stylus" - }, - ".sv": { - "color": "#DAE1C2", - "name": "SystemVerilog" - }, - ".svelte": { - "color": "#ff3e00", - "name": "Svelte" - }, - ".svh": { - "color": "#DAE1C2", - "name": "SystemVerilog" - }, - ".swift": { - "color": "#F05138", - "name": "Swift" - }, - ".t": { - "color": "#cf142b", - "name": "Turing" - }, - ".tac": { - "color": "#3572A5", - "name": "Python" - }, - ".tcc": { - "color": "#f34b7d", - "name": "C++" - }, - ".tcl": { - "color": "#e4cc98", - "name": "Tcl" - }, - ".tcl.in": { - "color": "#e4cc98", - "name": "Tcl" - }, - ".tesc": { - "color": "#5686a5", - "name": "GLSL" - }, - ".tese": { - "color": "#5686a5", - "name": "GLSL" - }, - ".tex": { - "color": "#3D6117", - "name": "TeX" - }, - ".thor": { - "color": "#701516", - "name": "Ruby" - }, - ".thrift": { - "color": "#D12127", - "name": "Thrift" - }, - ".thy": { - "color": "#FEFE00", - "name": "Isabelle" - }, - ".tla": { - "color": "#4b0079", - "name": "TLA" - }, - ".tm": { - "color": "#e4cc98", - "name": "Tcl" - }, - ".tmac": { - "color": "#ecdebe", - "name": "Roff" - }, - ".tmux": { - "color": "#89e051", - "name": "Shell" - }, - ".toc": { - "color": "#3D6117", - "name": "TeX" - }, - ".tool": { - "color": "#89e051", - "name": "Shell" - }, - ".tpb": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".tpl": { - "color": "#f0c040", - "name": "Smarty" - }, - ".tpp": { - "color": "#f34b7d", - "name": "C++" - }, - ".tps": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".trg": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".ts": { - "color": "#2b7489", - "name": "TypeScript" - }, - ".tst": { - "color": "#ca0f21", - "name": "Scilab" - }, - ".tsx": { - "color": "#2b7489", - "name": "TypeScript" - }, - ".tu": { - "color": "#cf142b", - "name": "Turing" - }, - ".twig": { - "color": "#c1d026", - "name": "Twig" - }, - ".txl": { - "color": "#0178b8", - "name": "TXL" - }, - ".uc": { - "color": "#a54c4d", - "name": "UnrealScript" - }, - ".udo": { - "color": "#1a1a1a", - "name": "Csound" - }, - ".uno": { - "color": "#9933cc", - "name": "Uno" - }, - ".upc": { - "color": "#4e3617", - "name": "Unified Parallel C" - }, - ".ur": { - "color": "#ccccee", - "name": "UrWeb" - }, - ".urs": { - "color": "#ccccee", - "name": "UrWeb" - }, - ".v": { - "color": "#b2b7f8", - "name": "Verilog" - }, - ".vala": { - "color": "#fbe5cd", - "name": "Vala" - }, - ".vapi": { - "color": "#fbe5cd", - "name": "Vala" - }, - ".vark": { - "color": "#82937f", - "name": "Gosu" - }, - ".vb": { - "color": "#945db7", - "name": "Visual Basic .NET" - }, - ".vba": { - "color": "#199f4b", - "name": "Vim Script" - }, - ".vbhtml": { - "color": "#945db7", - "name": "Visual Basic .NET" - }, - ".vbs": { - "color": "#15dcdc", - "name": "VBScript" - }, - ".vcl": { - "color": "#148AA8", - "name": "VCL" - }, - ".veo": { - "color": "#b2b7f8", - "name": "Verilog" - }, - ".vert": { - "color": "#5686a5", - "name": "GLSL" - }, - ".vh": { - "color": "#DAE1C2", - "name": "SystemVerilog" - }, - ".vhd": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".vhdl": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".vhf": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".vhi": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".vho": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".vhs": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".vht": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".vhw": { - "color": "#adb2cb", - "name": "VHDL" - }, - ".view.lkml": { - "color": "#652B81", - "name": "LookML" - }, - ".vim": { - "color": "#199f4b", - "name": "Vim Script" - }, - ".vmb": { - "color": "#199f4b", - "name": "Vim Script" - }, - ".volt": { - "color": "#1F1F1F", - "name": "Volt" - }, - ".vrx": { - "color": "#5686a5", - "name": "GLSL" - }, - ".vsh": { - "color": "#5686a5", - "name": "GLSL" - }, - ".vshader": { - "color": "#5686a5", - "name": "GLSL" - }, - ".vue": { - "color": "#41b883", - "name": "Vue" - }, - ".vw": { - "color": "#dad8d8", - "name": "PLSQL" - }, - ".w": { - "color": "#5ce600", - "name": "OpenEdge ABL" - }, - ".wast": { - "color": "#04133b", - "name": "WebAssembly" - }, - ".wat": { - "color": "#04133b", - "name": "WebAssembly" - }, - ".watchr": { - "color": "#701516", - "name": "Ruby" - }, - ".wdl": { - "color": "#42f1f4", - "name": "wdl" - }, - ".wisp": { - "color": "#7582D1", - "name": "wisp" - }, - ".wl": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".wlk": { - "color": "#a23738", - "name": "Wollok" - }, - ".wlt": { - "color": "#dd1100", - "name": "Mathematica" - }, - ".wlua": { - "color": "#000080", - "name": "Lua" - }, - ".wsgi": { - "color": "#3572A5", - "name": "Python" - }, - ".x10": { - "color": "#4B6BEF", - "name": "X10" - }, - ".x68": { - "color": "#005daa", - "name": "Motorola 68K Assembly" - }, - ".xc": { - "color": "#99DA07", - "name": "XC" - }, - ".xht": { - "color": "#e34c26", - "name": "HTML" - }, - ".xhtml": { - "color": "#e34c26", - "name": "HTML" - }, - ".xojo_code": { - "color": "#81bd41", - "name": "Xojo" - }, - ".xojo_menu": { - "color": "#81bd41", - "name": "Xojo" - }, - ".xojo_report": { - "color": "#81bd41", - "name": "Xojo" - }, - ".xojo_script": { - "color": "#81bd41", - "name": "Xojo" - }, - ".xojo_toolbar": { - "color": "#81bd41", - "name": "Xojo" - }, - ".xojo_window": { - "color": "#81bd41", - "name": "Xojo" - }, - ".xpy": { - "color": "#3572A5", - "name": "Python" - }, - ".xq": { - "color": "#5232e7", - "name": "XQuery" - }, - ".xql": { - "color": "#5232e7", - "name": "XQuery" - }, - ".xqm": { - "color": "#5232e7", - "name": "XQuery" - }, - ".xquery": { - "color": "#5232e7", - "name": "XQuery" - }, - ".xqy": { - "color": "#5232e7", - "name": "XQuery" - }, - ".xrl": { - "color": "#B83998", - "name": "Erlang" - }, - ".xsh": { - "color": "#285EEF", - "name": "Xonsh" - }, - ".xsjs": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".xsjslib": { - "color": "#f1e05a", - "name": "JavaScript" - }, - ".xsl": { - "color": "#EB8CEB", - "name": "XSLT" - }, - ".xslt": { - "color": "#EB8CEB", - "name": "XSLT" - }, - ".xtend": { - "color": "#24255d", - "name": "Xtend" - }, - ".xzap": { - "color": "#0d665e", - "name": "ZAP" - }, - ".y": { - "color": "#4B6C4B", - "name": "Yacc" - }, - ".yacc": { - "color": "#4B6C4B", - "name": "Yacc" - }, - ".yap": { - "color": "#74283c", - "name": "Prolog" - }, - ".yar": { - "color": "#220000", - "name": "YARA" - }, - ".yara": { - "color": "#220000", - "name": "YARA" - }, - ".yasnippet": { - "color": "#32AB90", - "name": "YASnippet" - }, - ".yrl": { - "color": "#B83998", - "name": "Erlang" - }, - ".yy": { - "color": "#4B6C4B", - "name": "Yacc" - }, - ".zap": { - "color": "#0d665e", - "name": "ZAP" - }, - ".zep": { - "color": "#118f9e", - "name": "Zephir" - }, - ".zig": { - "color": "#ec915c", - "name": "Zig" - }, - ".zil": { - "color": "#dc75e5", - "name": "ZIL" - }, - ".zimpl": { - "color": "#d67711", - "name": "Zimpl" - }, - ".zmpl": { - "color": "#d67711", - "name": "Zimpl" - }, - ".zpl": { - "color": "#d67711", - "name": "Zimpl" - }, - ".zs": { - "color": "#00BCD1", - "name": "ZenScript" - }, - ".zsh": { - "color": "#89e051", - "name": "Shell" - } -} \ No newline at end of file diff --git a/frontend/backend/src/data/github/graphql/__init__.py b/frontend/backend/src/data/github/graphql/__init__.py deleted file mode 100644 index 002a18ec..00000000 --- a/frontend/backend/src/data/github/graphql/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -from src.data.github.graphql.commit import get_commits -from src.data.github.graphql.models import RawCommit, RawRepo -from src.data.github.graphql.repo import get_repo -from src.data.github.graphql.template import ( - GraphQLErrorMissingNode, - GraphQLErrorRateLimit, - GraphQLErrorTimeout, - get_query_limit, -) -from src.data.github.graphql.user.contribs.contribs import ( - get_user_contribution_calendar, - get_user_contribution_events, -) -from src.data.github.graphql.user.contribs.models import ( - RawCalendar, - RawEvents, - RawEventsCommit, - RawEventsEvent, -) -from src.data.github.graphql.user.follows.follows import ( - get_user_followers, - get_user_following, -) -from src.data.github.graphql.user.follows.models import RawFollows - -__all__ = [ - "get_commits", - "RawCommit", - "RawRepo", - "get_repo", - "GraphQLErrorMissingNode", - "GraphQLErrorRateLimit", - "GraphQLErrorTimeout", - "get_query_limit", - "get_user_contribution_calendar", - "get_user_contribution_events", - "RawCalendar", - "RawEvents", - "RawEventsCommit", - "RawEventsEvent", - "get_user_followers", - "get_user_following", - "RawFollows", -] diff --git a/frontend/backend/src/data/github/graphql/commit.py b/frontend/backend/src/data/github/graphql/commit.py deleted file mode 100644 index 164bc1b7..00000000 --- a/frontend/backend/src/data/github/graphql/commit.py +++ /dev/null @@ -1,95 +0,0 @@ -from typing import List, Optional - -from src.constants import PR_FILES -from src.data.github.graphql.models import RawCommit -from src.data.github.graphql.template import ( - GraphQLError, - GraphQLErrorMissingNode, - GraphQLErrorRateLimit, - GraphQLErrorTimeout, - get_template, -) - - -def get_commits( - node_ids: List[str], access_token: Optional[str] = None, catch_errors: bool = False -) -> List[Optional[RawCommit]]: - """ - Gets all repository data from graphql - :param access_token: GitHub access token - :param node_ids: List of node ids - :return: List of commits - """ - - if PR_FILES == 0: # type: ignore - query = { - "variables": {"ids": node_ids}, - "query": """ - query getCommits($ids: [ID!]!) { - nodes(ids: $ids) { - ... on Commit { - additions - deletions - changedFiles - url - } - } - } - """, - } - else: - query = { - "variables": {"ids": node_ids, "first": PR_FILES}, - "query": """ - query getCommits($ids: [ID!]!, $first: Int!) { - nodes(ids: $ids) { - ... on Commit { - additions - deletions - changedFiles - url - associatedPullRequests(first: 1) { - nodes { - changedFiles - additions - deletions - files(first: $first) { - nodes { - path - additions - deletions - } - } - } - } - } - } - } - """, - } - - try: - raw_commits = get_template(query, access_token)["data"]["nodes"] - except GraphQLErrorMissingNode as e: - return ( - get_commits(node_ids[: e.node], access_token) - + [None] - + get_commits(node_ids[e.node + 1 :], access_token) - ) - except (GraphQLErrorRateLimit, GraphQLErrorTimeout, GraphQLError) as e: - if catch_errors: - return [None for _ in node_ids] - raise e - - out: List[Optional[RawCommit]] = [] - for raw_commit in raw_commits: - try: - if "associatedPullRequests" not in raw_commit: - raw_commit["associatedPullRequests"] = {"nodes": []} - out.append(RawCommit.model_validate(raw_commit)) - except Exception as e: - if catch_errors: - out.append(None) - else: - raise e - return out diff --git a/frontend/backend/src/data/github/graphql/models.py b/frontend/backend/src/data/github/graphql/models.py deleted file mode 100644 index f9122924..00000000 --- a/frontend/backend/src/data/github/graphql/models.py +++ /dev/null @@ -1,55 +0,0 @@ -from typing import List, Optional - -from pydantic import BaseModel, Field - - -class RawCommitPRFileNode(BaseModel): - path: str - additions: int - deletions: int - - -class RawCommitPRFile(BaseModel): - nodes: List[RawCommitPRFileNode] - - -class RawCommitPRNode(BaseModel): - changed_files: int = Field(alias="changedFiles") - additions: int - deletions: int - files: RawCommitPRFile - - -class RawCommitPR(BaseModel): - nodes: List[RawCommitPRNode] - - -class RawCommit(BaseModel): - additions: int - deletions: int - changed_files: int = Field(alias="changedFiles") - url: str - prs: RawCommitPR = Field(alias="associatedPullRequests") - - -class RawRepoLanguageNode(BaseModel): - name: str - color: Optional[str] - - -class RawRepoLanguageEdge(BaseModel): - node: RawRepoLanguageNode - size: int - - -class RawRepoLanguage(BaseModel): - total_count: int = Field(alias="totalCount") - total_size: int = Field(alias="totalSize") - edges: List[RawRepoLanguageEdge] - - -class RawRepo(BaseModel): - is_private: bool = Field(alias="isPrivate") - fork_count: int = Field(alias="forkCount") - stargazer_count: int = Field(alias="stargazerCount") - languages: RawRepoLanguage diff --git a/frontend/backend/src/data/github/graphql/repo.py b/frontend/backend/src/data/github/graphql/repo.py deleted file mode 100644 index 97868d8d..00000000 --- a/frontend/backend/src/data/github/graphql/repo.py +++ /dev/null @@ -1,50 +0,0 @@ -from typing import Optional - -from src.data.github.graphql.models import RawRepo -from src.data.github.graphql.template import get_template - - -def get_repo( - owner: str, - repo: str, - access_token: Optional[str] = None, - catch_errors: bool = False, -) -> Optional[RawRepo]: - """ - Gets all repository data from graphql - :param access_token: GitHub access token - :param owner: GitHub owner - :param repo: GitHub repository - :return: RawRepo object or None if repo not present - """ - query = { - "variables": {"owner": owner, "repo": repo}, - "query": """ - query getRepo($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - isPrivate, - forkCount, - stargazerCount, - languages(first: 10){ - totalCount, - totalSize, - edges{ - node { - name, - color, - }, - size, - }, - }, - } - } - """, - } - - try: - raw_repo = get_template(query, access_token)["data"]["repository"] - return RawRepo.model_validate(raw_repo) - except Exception as e: - if catch_errors: - return None - raise e diff --git a/frontend/backend/src/data/github/graphql/template.py b/frontend/backend/src/data/github/graphql/template.py deleted file mode 100644 index 45b924a7..00000000 --- a/frontend/backend/src/data/github/graphql/template.py +++ /dev/null @@ -1,102 +0,0 @@ -import logging -from datetime import datetime -from typing import Any, Dict, Optional, Tuple - -import requests -from requests.exceptions import ReadTimeout - -from src.constants import TIMEOUT -from src.data.github.utils import get_access_token - -s = requests.session() - - -class GraphQLError(Exception): - pass - - -class GraphQLErrorMissingNode(Exception): - def __init__(self, node: int, *args: Tuple[Any], **kwargs: Dict[str, Any]): - super(Exception, self).__init__(*args, **kwargs) - self.node = node - - -class GraphQLErrorRateLimit(Exception): - pass - - -class GraphQLErrorTimeout(Exception): - pass - - -def get_template( - query: Dict[str, Any], access_token: Optional[str] = None, retries: int = 0 -) -> Dict[str, Any]: - """ - Template for interacting with the GitHub GraphQL API - :param query: The query to be sent to the GitHub GraphQL API - :param access_token: The access token to be used for the query - :param retries: The number of retries to be made for Auth Exceptions - :return: The response from the GitHub GraphQL API - """ - start = datetime.now() - new_access_token = get_access_token(access_token) - headers: Dict[str, str] = {"Authorization": f"bearer {new_access_token}"} - - try: - r = s.post( - "https://api.github.com/graphql", - json=query, - headers=headers, - timeout=TIMEOUT, - ) - except ReadTimeout: - raise GraphQLErrorTimeout("GraphQL Error: Request Timeout") - - print("GraphQL", new_access_token, datetime.now() - start) - if r.status_code == 200: - data = r.json() - if "errors" in data: - if ( - "type" in data["errors"][0] - and data["errors"][0]["type"] in ["SERVICE_UNAVAILABLE", "NOT_FOUND"] - and "path" in data["errors"][0] - and isinstance(data["errors"][0]["path"], list) - and data["errors"][0]["path"][0] == "nodes" - ): - raise GraphQLErrorMissingNode(node=int(data["errors"][0]["path"][1])) - - if retries < 2: - print("GraphQL Error, Retrying:", new_access_token) - return get_template(query, access_token, retries + 1) - - raise GraphQLError("GraphQL Error: " + str(data["errors"])) - - return data - - if r.status_code in [401, 403]: - if retries < 2: - print("GraphQL Error, Retrying:", new_access_token) - return get_template(query, access_token, retries + 1) - raise GraphQLErrorRateLimit("GraphQL Error: Unauthorized") - - if r.status_code == 502: - raise GraphQLErrorTimeout("GraphQL Error: Request Timeout") - - raise GraphQLError(f"GraphQL Error: {str(r.status_code)}") - - -def get_query_limit(access_token: str) -> int: - """ - Get the current rate limit for the GitHub GraphQL API - :param access_token: The access token to be used for the query - :return: The current rate limit for the GitHub GraphQL API - """ - try: - data = get_template( - {"query": "query { rateLimit { remaining } }"}, access_token - ) - return data["data"]["rateLimit"]["remaining"] - except Exception as e: - logging.exception(e) - return -1 diff --git a/frontend/backend/src/data/github/graphql/user/__init__.py b/frontend/backend/src/data/github/graphql/user/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/data/github/graphql/user/contribs/__init__.py b/frontend/backend/src/data/github/graphql/user/contribs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/data/github/graphql/user/contribs/contribs.py b/frontend/backend/src/data/github/graphql/user/contribs/contribs.py deleted file mode 100644 index d2e33c8f..00000000 --- a/frontend/backend/src/data/github/graphql/user/contribs/contribs.py +++ /dev/null @@ -1,157 +0,0 @@ -# import json -from datetime import datetime -from typing import Optional - -from src.data.github.graphql.template import get_template -from src.data.github.graphql.user.contribs.models import RawCalendar, RawEvents - - -def get_user_contribution_calendar( - user_id: str, - start_date: datetime, - end_date: datetime, - access_token: Optional[str] = None, -) -> RawCalendar: - """Gets contribution calendar for a given time period (max one year)""" - if (end_date - start_date).days > 365: - raise ValueError("date range can be at most 1 year") - query = { - "variables": { - "login": user_id, - "startDate": start_date.strftime("%Y-%m-%dT%H:%M:%SZ"), - "endDate": end_date.strftime("%Y-%m-%dT%H:%M:%SZ"), - }, - "query": """ - query getUser($login: String!, $startDate: DateTime!, $endDate: DateTime!){ - user(login: $login){ - contributionsCollection(from: $startDate, to: $endDate){ - contributionCalendar{ - weeks{ - contributionDays{ - date - weekday - contributionCount - } - } - } - } - } - } - """, - } - - raw_data = get_template(query, access_token) - output = raw_data["data"]["user"]["contributionsCollection"]["contributionCalendar"] - return RawCalendar.model_validate(output) - - -def get_user_contribution_events( - user_id: str, - start_date: datetime, - end_date: datetime, - max_repos: int = 100, - first: int = 100, - after: str = "", - access_token: Optional[str] = None, -) -> RawEvents: - """Fetches user contributions (commits, issues, prs, reviews)""" - query = { - "variables": { - "login": user_id, - "startDate": start_date.strftime("%Y-%m-%dT%H:%M:%SZ"), - "endDate": end_date.strftime("%Y-%m-%dT%H:%M:%SZ"), - "maxRepos": max_repos, - "first": first, - "after": after, - }, - "query": """ - query getUser($login: String!, $startDate: DateTime!, $endDate: DateTime!, $maxRepos: Int!, $first: Int!, $after: String!) { - user(login: $login){ - contributionsCollection(from: $startDate, to: $endDate){ - commitContributionsByRepository(maxRepositories: $maxRepos){ - repository{ - nameWithOwner, - }, - totalCount:contributions(first: 1){ - totalCount - } - contributions(first: $first, after: $after){ - nodes{ - commitCount, - occurredAt, - } - pageInfo{ - hasNextPage, - endCursor - } - } - } - issueContributionsByRepository(maxRepositories: $maxRepos){ - repository{ - nameWithOwner - }, - totalCount:contributions(first: 1){ - totalCount - } - contributions(first: $first, after: $after){ - nodes{ - occurredAt, - } - pageInfo{ - hasNextPage, - endCursor - } - } - } - pullRequestContributionsByRepository(maxRepositories: $maxRepos){ - repository{ - nameWithOwner - }, - totalCount:contributions(first: 1){ - totalCount - } - contributions(first: $first, after: $after){ - nodes{ - occurredAt, - } - pageInfo{ - hasNextPage, - endCursor - } - } - } - pullRequestReviewContributionsByRepository(maxRepositories: $maxRepos){ - repository{ - nameWithOwner - }, - totalCount:contributions(first: 1){ - totalCount - } - contributions(first: $first, after: $after){ - nodes{ - occurredAt, - } - pageInfo{ - hasNextPage, - endCursor - } - } - }, - repositoryContributions(first: $maxRepos){ - totalCount - nodes{ - repository{ - nameWithOwner, - } - occurredAt, - } - }, - }, - } - } - """, - } - - raw_data = get_template(query, access_token) - output = raw_data["data"]["user"]["contributionsCollection"] - return RawEvents.model_validate(output) diff --git a/frontend/backend/src/data/github/graphql/user/contribs/models.py b/frontend/backend/src/data/github/graphql/user/contribs/models.py deleted file mode 100644 index e541ff61..00000000 --- a/frontend/backend/src/data/github/graphql/user/contribs/models.py +++ /dev/null @@ -1,91 +0,0 @@ -from datetime import date, datetime -from typing import List, Optional - -from pydantic import BaseModel, Field - - -class RawCalendarDay(BaseModel): - date: date - weekday: int - count: int = Field(alias="contributionCount") - - -class RawCalendarWeek(BaseModel): - contribution_days: List[RawCalendarDay] = Field(alias="contributionDays") - - -class RawCalendar(BaseModel): - weeks: List[RawCalendarWeek] - - -class RawEventsRepoName(BaseModel): - name: str = Field(alias="nameWithOwner") - - -class RawEventsCount(BaseModel): - count: int = Field(alias="totalCount") - - -class RawEventsCommit(BaseModel): - count: int = Field(alias="commitCount") - occurred_at: datetime = Field(alias="occurredAt") - - -class RawEventsEvent(BaseModel): - occurred_at: datetime = Field(alias="occurredAt") - - -class RawEventsPageInfo(BaseModel): - has_next_page: bool = Field(alias="hasNextPage") - end_cursor: Optional[str] = Field(alias="endCursor") - - class Config: - allow_none = True - - -class RawEventsCommits(BaseModel): - nodes: List[RawEventsCommit] - page_info: RawEventsPageInfo = Field(alias="pageInfo") - - -class RawEventsContribs(BaseModel): - nodes: List[RawEventsEvent] - page_info: RawEventsPageInfo = Field(alias="pageInfo") - - -class RawEventsRepoCommits(BaseModel): - repo: RawEventsRepoName = Field(alias="repository") - count: RawEventsCount = Field(alias="totalCount") - contribs: RawEventsCommits = Field(alias="contributions") - - -class RawEventsRepo(BaseModel): - repo: RawEventsRepoName = Field(alias="repository") - count: RawEventsCount = Field(alias="totalCount") - contribs: RawEventsContribs = Field(alias="contributions") - - -class RawEventsRepoEvent(BaseModel): - repo: RawEventsRepoName = Field(alias="repository") - occurred_at: datetime = Field(alias="occurredAt") - - -class RawEventsRepoContribs(BaseModel): - count: int = Field(alias="totalCount") - nodes: List[RawEventsRepoEvent] - - -class RawEvents(BaseModel): - commit_contribs_by_repo: List[RawEventsRepoCommits] = Field( - alias="commitContributionsByRepository" - ) - issue_contribs_by_repo: List[RawEventsRepo] = Field( - alias="issueContributionsByRepository" - ) - pr_contribs_by_repo: List[RawEventsRepo] = Field( - alias="pullRequestContributionsByRepository" - ) - review_contribs_by_repo: List[RawEventsRepo] = Field( - alias="pullRequestReviewContributionsByRepository" - ) - repo_contribs: RawEventsRepoContribs = Field(alias="repositoryContributions") diff --git a/frontend/backend/src/data/github/graphql/user/follows/__init__.py b/frontend/backend/src/data/github/graphql/user/follows/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/data/github/graphql/user/follows/follows.py b/frontend/backend/src/data/github/graphql/user/follows/follows.py deleted file mode 100644 index 9546b368..00000000 --- a/frontend/backend/src/data/github/graphql/user/follows/follows.py +++ /dev/null @@ -1,121 +0,0 @@ -# import json -from typing import Dict, Optional, Union - -from src.data.github.graphql.template import get_template -from src.data.github.graphql.user.follows.models import RawFollows - - -def get_user_followers( - user_id: str, first: int = 100, after: str = "", access_token: Optional[str] = None -) -> RawFollows: - """gets user's followers and users following'""" - - variables: Dict[str, Union[str, int]] = ( - {"login": user_id, "first": first, "after": after} - if after != "" - else {"login": user_id, "first": first} - ) - - query_str: str = ( - """ - query getUser($login: String!, $first: Int!, $after: String!) { - user(login: $login){ - followers(first: $first, after: $after){ - nodes{ - name, - login, - url - } - pageInfo{ - hasNextPage, - endCursor - } - } - } - } - """ - if after != "" - else """ - query getUser($login: String!, $first: Int!) { - user(login: $login){ - followers(first: $first){ - nodes{ - name, - login, - url - } - pageInfo{ - hasNextPage, - endCursor - } - } - } - } - """ - ) - - query = { - "variables": variables, - "query": query_str, - } - - output_dict = get_template(query, access_token)["data"]["user"]["followers"] - return RawFollows.model_validate(output_dict) - - -def get_user_following( - user_id: str, first: int = 10, after: str = "", access_token: Optional[str] = None -) -> RawFollows: - """gets user's followers and users following'""" - - variables: Dict[str, Union[str, int]] = ( - {"login": user_id, "first": first, "after": after} - if after != "" - else {"login": user_id, "first": first} - ) - - query_str: str = ( - """ - query getUser($login: String!, $first: Int!, $after: String!) { - user(login: $login){ - following(first: $first, after: $after){ - nodes{ - name, - login, - url - } - pageInfo{ - hasNextPage, - endCursor - } - } - } - } - """ - if after != "" - else """ - query getUser($login: String!, $first: Int!) { - user(login: $login){ - following(first: $first){ - nodes{ - name, - login, - url - } - pageInfo{ - hasNextPage, - endCursor - } - } - } - } - """ - ) - - query = { - "variables": variables, - "query": query_str, - } - - output_dict = get_template(query, access_token)["data"]["user"]["following"] - return RawFollows.model_validate(output_dict) diff --git a/frontend/backend/src/data/github/graphql/user/follows/models.py b/frontend/backend/src/data/github/graphql/user/follows/models.py deleted file mode 100644 index 82fcaac0..00000000 --- a/frontend/backend/src/data/github/graphql/user/follows/models.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import List, Optional - -from pydantic import BaseModel, Field - -from src.models import User - - -class PageInfo(BaseModel): - has_next_page: bool = Field(alias="hasNextPage") - end_cursor: Optional[str] = Field(alias="endCursor") - - class Config: - allow_none = True - - -class RawFollows(BaseModel): - nodes: List[User] - page_info: PageInfo = Field(alias="pageInfo") diff --git a/frontend/backend/src/data/github/language_map.py b/frontend/backend/src/data/github/language_map.py deleted file mode 100644 index 920b9936..00000000 --- a/frontend/backend/src/data/github/language_map.py +++ /dev/null @@ -1,30 +0,0 @@ -import json -import urllib.request -from typing import Any, Dict - -BLACKLIST = [".md"] - -with urllib.request.urlopen( - "https://raw.githubusercontent.com/blakeembrey/language-map/main/languages.json" -) as url: - data: Dict[str, Dict[str, Any]] = json.loads(url.read().decode()) - languages = { - k: v - for k, v in data.items() - if v["type"] in ["programming", "markup"] and "color" in v and "extensions" in v - } - extensions: Dict[str, Dict[str, str]] = {} - for lang_name, lang in languages.items(): - for extension in lang["extensions"]: - if extension not in BLACKLIST: - extensions[extension] = {"color": lang["color"], "name": lang_name} - extensions = dict(sorted(extensions.items(), key=lambda x: x[0])) - extensions[".tsx"]["name"] = "TypeScript" - extensions[".tsx"]["color"] = "#2B7489" - extensions[".cs"]["name"] = "C#" - extensions[".cs"]["color"] = "#178600" - extensions[".ml"]["name"] = "OCaml" - extensions[".ml"]["color"] = "#3BE133" - - with open("src/data/github/extensions.json", "w") as f: - json.dump(extensions, f, indent=4) diff --git a/frontend/backend/src/data/github/rest/__init__.py b/frontend/backend/src/data/github/rest/__init__.py deleted file mode 100644 index 008de45e..00000000 --- a/frontend/backend/src/data/github/rest/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from src.data.github.rest.commit import get_commit_files -from src.data.github.rest.models import RawCommit, RawCommitFile -from src.data.github.rest.repo import get_repo_commits, get_repo_stargazers -from src.data.github.rest.template import RESTError, RESTErrorNotFound -from src.data.github.rest.user import get_user, get_user_starred_repos - -__all__ = [ - "get_commit_files", - "RawCommit", - "RawCommitFile", - "get_repo_commits", - "get_repo_stargazers", - "RESTError", - "RESTErrorNotFound", - "get_user", - "get_user_starred_repos", -] diff --git a/frontend/backend/src/data/github/rest/commit.py b/frontend/backend/src/data/github/rest/commit.py deleted file mode 100644 index a61a2a1e..00000000 --- a/frontend/backend/src/data/github/rest/commit.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import List, Optional - -from src.data.github.rest.models import RawCommitFile -from src.data.github.rest.template import get_template - -BASE_URL = "https://api.github.com/repos/" - - -def get_commit_files( - owner: str, repo: str, sha: str, access_token: Optional[str] = None -) -> Optional[List[RawCommitFile]]: - """ - Returns raw repository data - :param owner: repository owner - :param repo: repository name - :param sha: commit sha - :param access_token: GitHub access token - :return: repository data - """ - - try: - output = get_template( - BASE_URL + owner + "/" + repo + "/commits/" + sha, access_token - ) - files = output["files"] - return [RawCommitFile.model_validate(f) for f in files] - except Exception: - return None diff --git a/frontend/backend/src/data/github/rest/models.py b/frontend/backend/src/data/github/rest/models.py deleted file mode 100644 index d441d84a..00000000 --- a/frontend/backend/src/data/github/rest/models.py +++ /dev/null @@ -1,14 +0,0 @@ -from datetime import datetime - -from pydantic import BaseModel - - -class RawCommit(BaseModel): - timestamp: datetime - node_id: str - - -class RawCommitFile(BaseModel): - filename: str - additions: int - deletions: int diff --git a/frontend/backend/src/data/github/rest/repo.py b/frontend/backend/src/data/github/rest/repo.py deleted file mode 100644 index a394259b..00000000 --- a/frontend/backend/src/data/github/rest/repo.py +++ /dev/null @@ -1,174 +0,0 @@ -import logging -from datetime import datetime -from typing import Any, Dict, List, Optional - -from src.data.github.rest.models import RawCommit -from src.data.github.rest.template import RESTError, get_template, get_template_plural - -BASE_URL = "https://api.github.com/repos/" - - -# NOTE: unused, untested -def get_repo(access_token: str, owner: str, repo: str) -> Dict[str, Any]: - """ - Returns raw repository data - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :return: repository data - """ - return get_template(BASE_URL + owner + "/" + repo, access_token) - - -# NOTE: unused, untested -def get_repo_languages( - access_token: str, owner: str, repo: str -) -> List[Dict[str, Any]]: - """ - Returns repository language breakdown - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :return: repository language breakdown - """ - return get_template_plural( - BASE_URL + owner + "/" + repo + "/languages", access_token - ) - - -def get_repo_stargazers( - access_token: str, owner: str, repo: str, per_page: int = 100, page: int = 1 -) -> List[Dict[str, Any]]: - """ - Returns stargazers with timestamp for repository - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :param per_page: number of items per page - :param page: page number - :return: stargazers with timestamp for repository - """ - return get_template_plural( - BASE_URL + owner + "/" + repo + "/stargazers", - access_token, - per_page=per_page, - page=page, - accept_header="applicaiton/vnd.github.v3.star+json", - ) - - -# NOTE: unused, untested -# does not accept per page, exceeds if necessary -def get_repo_code_frequency(access_token: str, owner: str, repo: str) -> Dict[str, Any]: - """ - Returns code frequency for repository - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :return: code frequency for repository - """ - return get_template( - BASE_URL + owner + "/" + repo + "/stats/code_frequency", access_token - ) - - -# NOTE: unused, untested -def get_repo_commit_activity( - access_token: str, owner: str, repo: str -) -> Dict[str, Any]: - """ - Returns commit activity for past year, broken by week - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :return: commit activity for past year, broken by week - """ - return get_template( - BASE_URL + owner + "/" + repo + "/stats/commit_activity", access_token - ) - - -# NOTE: unused, untested -def get_repo_contributors(access_token: str, owner: str, repo: str) -> Dict[str, Any]: - """ - Returns contributors for a repository - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :return: contributors for a repository - """ - return get_template( - BASE_URL + owner + "/" + repo + "/stats/contributors", access_token - ) - - -# NOTE: unused, untested -def get_repo_weekly_commits(access_token: str, owner: str, repo: str) -> Dict[str, Any]: - """ - Returns contributions by week, owner/non-owner - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :return: contributions by week, owner/non-owner - """ - return get_template( - BASE_URL + owner + "/" + repo + "/stats/participation", access_token - ) - - -# NOTE: unused, untested -def get_repo_hourly_commits(access_token: str, owner: str, repo: str) -> Dict[str, Any]: - """ - Returns contributions by day, hour for repository - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - """ - return get_template( - BASE_URL + owner + "/" + repo + "/stats/punch_card", access_token - ) - - -def get_repo_commits( - owner: str, - repo: str, - user: Optional[str] = None, - since: Optional[datetime] = None, - until: Optional[datetime] = None, - page: int = 1, - access_token: Optional[str] = None, -) -> List[RawCommit]: - """ - Returns most recent commits - :param access_token: GitHub access token - :param owner: repository owner - :param repo: repository name - :param user: optional GitHub user if not owner - :param since: optional datetime to start from - :param until: optional datetime to end at - :param page: optional page number - :return: Up to 100 commits from page - """ - user = user if user is not None else owner - query = BASE_URL + owner + "/" + repo + "/commits?author=" + user - if since is not None: - query += f"&since={str(since)}" - if until is not None: - query += f"&until={str(until)}" - try: - data = get_template_plural(query, access_token, page=page) - - def extract_info(x: Any) -> RawCommit: - dt = x["commit"]["committer"]["date"] - temp = { - "timestamp": datetime.strptime(dt, "%Y-%m-%dT%H:%M:%SZ"), - "node_id": x["node_id"], - } - return RawCommit.model_validate(temp) - - return list(map(extract_info, data)) - except RESTError: - return [] - except Exception as e: - logging.exception(e) - return [] diff --git a/frontend/backend/src/data/github/rest/template.py b/frontend/backend/src/data/github/rest/template.py deleted file mode 100644 index b82185cd..00000000 --- a/frontend/backend/src/data/github/rest/template.py +++ /dev/null @@ -1,112 +0,0 @@ -from datetime import datetime -from typing import Any, Dict, List, Optional - -import requests -from requests.exceptions import ReadTimeout - -from src.constants import TIMEOUT -from src.data.github.utils import get_access_token - -s = requests.session() - - -class RESTError(Exception): - pass - - -class RESTErrorUnauthorized(RESTError): - pass - - -class RESTErrorNotFound(RESTError): - pass - - -class RESTErrorEmptyRepo(RESTError): - pass - - -class RESTErrorTimeout(RESTError): - pass - - -def _get_template( - query: str, - params: Dict[str, Any], - accept_header: str, - access_token: Optional[str] = None, - retries: int = 0, -) -> Any: - """ - Internal template for interacting with the GitHub REST API - :param query: The query to be sent to the GitHub API - :param params: The parameters to be sent to the GitHub API - :param access_token: The access token to be sent to the GitHub API - :param accept_header: The accept header to be sent to the GitHub API - :return: The response from the GitHub API - """ - start = datetime.now() - - new_access_token = get_access_token(access_token) - headers: Dict[str, str] = { - "Accept": accept_header, - "Authorization": f"bearer {new_access_token}", - } - - try: - r = s.get(query, params=params, headers=headers, timeout=TIMEOUT) - except ReadTimeout: - raise RESTErrorTimeout("REST Error: Request Timeout") - - if r.status_code == 200: - print("REST API", new_access_token, datetime.now() - start) - return r.json() - - if r.status_code == 401: - raise RESTErrorUnauthorized("REST Error: Unauthorized") - - if r.status_code == 404: - raise RESTErrorNotFound("REST Error: Not Found") - - if r.status_code == 409: - raise RESTErrorEmptyRepo("REST Error: Empty Repository") - - if retries < 3: - print("REST Error, Retrying:", new_access_token) - return _get_template(query, params, accept_header, access_token, retries + 1) - raise RESTError(f"REST Error: {str(r.status_code)}") - - -def get_template( - query: str, - access_token: Optional[str] = None, - accept_header: str = "application/vnd.github.v3+json", -) -> Dict[str, Any]: - """ - Template for interacting with the GitHub REST API (singular) - :param query: The query to be sent to the GitHub API - :param access_token: The access token to be sent to the GitHub API - :param accept_header: The accept header to be sent to the GitHub API - :return: The response from the GitHub API - """ - return _get_template(query, {}, accept_header, access_token) - - -def get_template_plural( - query: str, - access_token: Optional[str] = None, - per_page: int = 100, - page: int = 1, - accept_header: str = "application/vnd.github.v3+json", -) -> List[Dict[str, Any]]: - """ - Template for interacting with the GitHub REST API (plural) - :param query: The query to be sent to the GitHub API - :param access_token: The access token to be sent to the GitHub API - :param per_page: The number of items to be returned per page - :param page: The page number to be returned - :param accept_header: The accept header to be sent to the GitHub API - :return: The response from the GitHub API - """ - params: Dict[str, str] = {"per_page": str(per_page), "page": str(page)} - return _get_template(query, params, accept_header, access_token) diff --git a/frontend/backend/src/data/github/rest/user.py b/frontend/backend/src/data/github/rest/user.py deleted file mode 100644 index 527049b5..00000000 --- a/frontend/backend/src/data/github/rest/user.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Any, Dict, List - -from src.data.github.rest.template import get_template, get_template_plural - -BASE_URL = "https://api.github.com/users/" - - -def get_user(user_id: str, access_token: str) -> Dict[str, Any]: - """ - Returns raw user data - :param user_id: GitHub user id - :param access_token: GitHub access token - """ - return get_template(BASE_URL + user_id, access_token) - - -def get_user_starred_repos( - user_id: str, access_token: str, per_page: int = 100, page: int = 1 -) -> List[Dict[str, Any]]: - """ - Returns list of starred repos - :param user_id: GitHub user id - :param access_token: GitHub access token - :param per_page: number of repos to return per page - """ - return get_template_plural( - BASE_URL + user_id + "/starred", - access_token, - per_page=per_page, - page=page, - accept_header="application/vnd.github.v3.star+json", - ) diff --git a/frontend/backend/src/data/github/utils.py b/frontend/backend/src/data/github/utils.py deleted file mode 100644 index f3975892..00000000 --- a/frontend/backend/src/data/github/utils.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Optional - -from src.data.mongo.secret import get_random_key - - -def get_access_token(access_token: Optional[str] = None) -> str: - return access_token if access_token is not None else get_random_key() diff --git a/frontend/backend/src/data/mongo/__init__.py b/frontend/backend/src/data/mongo/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/data/mongo/main.py b/frontend/backend/src/data/mongo/main.py deleted file mode 100644 index c8964063..00000000 --- a/frontend/backend/src/data/mongo/main.py +++ /dev/null @@ -1,29 +0,0 @@ -from motor.core import AgnosticCollection -from motor.motor_asyncio import AsyncIOMotorClient - -from src.constants import LOCAL, MONGODB_PASSWORD, PROD - - -def get_conn_str(password: str, database: str) -> str: - return f"mongodb+srv://root:{password}@backend2.e50j8dp.mongodb.net/{database}?retryWrites=true&w=majority" - - -if LOCAL: - DB = None -elif PROD: - conn_str = get_conn_str(MONGODB_PASSWORD, "prod_backend") - CLIENT = AsyncIOMotorClient( - conn_str, serverSelectionTimeoutMS=5000, tlsInsecure=True - ) - DB = CLIENT.prod_backend # type: ignore -else: - conn_str = get_conn_str(MONGODB_PASSWORD, "dev_backend") - CLIENT = AsyncIOMotorClient( # type: ignore - conn_str, serverSelectionTimeoutMS=5000, tlsInsecure=True - ) - DB = CLIENT.dev_backend # type: ignore - -# Overwrite type since only None if Local=True -SECRETS: AgnosticCollection = None if DB is None else DB.secrets # type: ignore -USERS: AgnosticCollection = None if DB is None else DB.users # type: ignore -USER_MONTHS: AgnosticCollection = None if DB is None else DB.user_months # type: ignore diff --git a/frontend/backend/src/data/mongo/secret/__init__.py b/frontend/backend/src/data/mongo/secret/__init__.py deleted file mode 100644 index 2ed1ba10..00000000 --- a/frontend/backend/src/data/mongo/secret/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from src.data.mongo.secret.functions import get_random_key, update_keys - -__all__ = ["get_random_key", "update_keys"] diff --git a/frontend/backend/src/data/mongo/secret/functions.py b/frontend/backend/src/data/mongo/secret/functions.py deleted file mode 100644 index c3f0bb1b..00000000 --- a/frontend/backend/src/data/mongo/secret/functions.py +++ /dev/null @@ -1,35 +0,0 @@ -from datetime import timedelta -from random import randint -from typing import Any, Dict, List, Optional, Tuple - -from src.constants import TEST_TOKEN -from src.data.mongo.main import SECRETS -from src.data.mongo.secret.models import SecretModel -from src.utils import alru_cache - - -@alru_cache(ttl=timedelta(minutes=15)) -async def get_keys(no_cache: bool = False) -> Tuple[bool, List[str]]: - return (False, []) - secrets: Optional[Dict[str, Any]] = await SECRETS.find_one({"project": "main"}) - if secrets is None: - return (False, []) - - tokens = SecretModel.model_validate(secrets).access_tokens - return (True, tokens) - - -secret_keys: List[str] = [] - - -async def update_keys(no_cache: bool = False) -> None: - global secret_keys - secret_keys = await get_keys(no_cache=no_cache) - - -def get_random_key() -> str: - global secret_keys - if len(secret_keys) == 0: - return TEST_TOKEN - - return secret_keys[randint(0, len(secret_keys) - 1)] diff --git a/frontend/backend/src/data/mongo/secret/models.py b/frontend/backend/src/data/mongo/secret/models.py deleted file mode 100644 index c3d1eff9..00000000 --- a/frontend/backend/src/data/mongo/secret/models.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import List - -from pydantic import BaseModel - - -class SecretModel(BaseModel): - project: str - access_tokens: List[str] diff --git a/frontend/backend/src/data/mongo/user/__init__.py b/frontend/backend/src/data/mongo/user/__init__.py deleted file mode 100644 index a33c74fa..00000000 --- a/frontend/backend/src/data/mongo/user/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from src.data.mongo.user.functions import delete_user, is_user_key, update_user -from src.data.mongo.user.get import get_full_user, get_public_user -from src.data.mongo.user.models import FullUserModel, PublicUserModel - -__all__ = [ - "delete_user", - "is_user_key", - "update_user", - "get_full_user", - "get_public_user", - "FullUserModel", - "PublicUserModel", -] diff --git a/frontend/backend/src/data/mongo/user/functions.py b/frontend/backend/src/data/mongo/user/functions.py deleted file mode 100644 index 74b7f7fa..00000000 --- a/frontend/backend/src/data/mongo/user/functions.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Any, Dict, Optional - -from src.data.mongo.main import USERS - - -async def is_user_key(user_id: str, user_key: str) -> bool: - user: Optional[dict[str, str]] = await USERS.find_one( - {"user_id": user_id}, {"user_key": 1} - ) - return user is not None and user.get("user_key", "") == user_key - - -async def update_user(user_id: str, raw_user: Dict[str, Any]) -> None: - await USERS.update_one( - {"user_id": user_id}, - {"$set": raw_user}, - upsert=True, - ) - - -async def delete_user(user_id: str, user_key: str, use_user_key: bool = True) -> bool: - if use_user_key: - is_key = await is_user_key(user_id, user_key) - if not is_key: - return False - - await USERS.delete_one({"user_id": user_id}) - return True diff --git a/frontend/backend/src/data/mongo/user/get.py b/frontend/backend/src/data/mongo/user/get.py deleted file mode 100644 index 6b8ef812..00000000 --- a/frontend/backend/src/data/mongo/user/get.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import Any, Dict, Optional, Tuple - -from pydantic import ValidationError - -from src.data.mongo.main import USERS -from src.data.mongo.user.models import FullUserModel, PublicUserModel -from src.utils import alru_cache - - -@alru_cache() -async def get_public_user( - user_id: str, no_cache: bool = False -) -> Tuple[bool, Optional[PublicUserModel]]: - return (False, None) - user: Optional[Dict[str, Any]] = await USERS.find_one({"user_id": user_id}) - if user is None: - # flag is false, don't cache - return (False, None) - try: - return (True, PublicUserModel.model_validate(user)) - except (TypeError, KeyError, ValidationError): - return (False, None) - - -@alru_cache() -async def get_full_user( - user_id: str, no_cache: bool = False -) -> Tuple[bool, Optional[FullUserModel]]: - user: Optional[Dict[str, Any]] = await USERS.find_one({"user_id": user_id}) - - if user is None: - # flag is false, don't cache - return (False, None) - - try: - return (True, FullUserModel.model_validate(user)) - except (TypeError, KeyError, ValidationError): - return (False, None) diff --git a/frontend/backend/src/data/mongo/user/models.py b/frontend/backend/src/data/mongo/user/models.py deleted file mode 100644 index 26ef9320..00000000 --- a/frontend/backend/src/data/mongo/user/models.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel, validator - - -class PublicUserModel(BaseModel): - user_id: str - access_token: str - private_access: Optional[bool] - - class Config: - from_attributes = True - validate_assignment = True - - @validator("private_access", pre=True, always=True) - def set_name(cls, private_access: Optional[bool]): - return False if private_access is None else private_access - - -class FullUserModel(PublicUserModel): - user_key: Optional[str] diff --git a/frontend/backend/src/data/mongo/user_months/__init__.py b/frontend/backend/src/data/mongo/user_months/__init__.py deleted file mode 100644 index 1619d06e..00000000 --- a/frontend/backend/src/data/mongo/user_months/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from src.data.mongo.user_months.functions import set_user_month -from src.data.mongo.user_months.get import get_user_months -from src.data.mongo.user_months.models import UserMonth - -__all__ = ["set_user_month", "get_user_months", "UserMonth"] diff --git a/frontend/backend/src/data/mongo/user_months/functions.py b/frontend/backend/src/data/mongo/user_months/functions.py deleted file mode 100644 index 4a38def9..00000000 --- a/frontend/backend/src/data/mongo/user_months/functions.py +++ /dev/null @@ -1,14 +0,0 @@ -from src.data.mongo.main import USER_MONTHS -from src.data.mongo.user_months.models import UserMonth - - -async def set_user_month(user_month: UserMonth): - return - compressed_user_month = user_month.model_dump() - compressed_user_month["data"] = user_month.data.compress() - - await USER_MONTHS.update_one( - {"user_id": user_month.user_id, "month": user_month.month}, - {"$set": compressed_user_month}, - upsert=True, - ) diff --git a/frontend/backend/src/data/mongo/user_months/get.py b/frontend/backend/src/data/mongo/user_months/get.py deleted file mode 100644 index 8869ea48..00000000 --- a/frontend/backend/src/data/mongo/user_months/get.py +++ /dev/null @@ -1,53 +0,0 @@ -from datetime import date, datetime -from typing import Any, Dict, List - -from src.constants import API_VERSION, USER_WHITELIST -from src.data.mongo.main import USER_MONTHS -from src.data.mongo.user_months.models import UserMonth -from src.models import UserPackage - - -async def get_user_months( - user_id: str, private_access: bool, start_month: date, end_month: date -) -> List[UserMonth]: - return [] - start = datetime(start_month.year, start_month.month, 1) - end = datetime(end_month.year, end_month.month, 28) - today = datetime.now() - - filters = { - "user_id": user_id, - "month": {"$gte": start, "$lte": end}, - "version": API_VERSION, - } - - if private_access: - filters["private"] = True - - months: List[Dict[str, Any]] = await USER_MONTHS.find(filters).to_list(length=None) # type: ignore - - months_data: List[UserMonth] = [] - for month in months: - date_obj: datetime = month["month"] - complete = ( - not (date_obj.year == today.year and date_obj.month == today.month) - or user_id in USER_WHITELIST - ) - try: - data = UserPackage.decompress(month["data"]) - months_data.append( - UserMonth.model_validate( - { - "user_id": user_id, - "month": month["month"], - "version": API_VERSION, - "private": month["private"], - "complete": complete, - "data": data.model_dump(), - } - ) - ) - except Exception: - pass - - return months_data diff --git a/frontend/backend/src/data/mongo/user_months/models.py b/frontend/backend/src/data/mongo/user_months/models.py deleted file mode 100644 index 76ff34bd..00000000 --- a/frontend/backend/src/data/mongo/user_months/models.py +++ /dev/null @@ -1,14 +0,0 @@ -from datetime import datetime - -from pydantic import BaseModel - -from src.models import UserPackage - - -class UserMonth(BaseModel): - user_id: str - month: datetime - version: float - private: bool - complete: bool - data: UserPackage diff --git a/frontend/backend/src/main.py b/frontend/backend/src/main.py deleted file mode 100644 index 07df7095..00000000 --- a/frontend/backend/src/main.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Dict - -import sentry_sdk -from dotenv import find_dotenv, load_dotenv -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware -from sentry_sdk.integrations.asgi import SentryAsgiMiddleware - -load_dotenv(find_dotenv()) - -# flake8: noqa E402 - -# add endpoints here (after load dotenv) -from src.constants import PROD, SENTRY_DSN -from src.routers import ( - asset_router, - auth_router, - dev_router, - user_router, - wrapped_router, -) - -""" -SETUP -""" - -app = FastAPI() - -origins = [ - "http://localhost:3000", - "http://localhost:3001", - "https://githubtrends.io", - "https://www.githubtrends.io", - "https://githubwrapped.io", - "https://www.githubwrapped.io", -] - -app.add_middleware( - CORSMiddleware, - allow_origins=origins, - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -sentry_sdk.init( - SENTRY_DSN, - traces_sample_rate=(1.0 if PROD else 1.0), -) - -app.add_middleware( - SentryAsgiMiddleware, -) - - -@app.get("/") -async def read_root() -> Dict[str, str]: - return {"Hello": "World"} - - -@app.get("/info") -def get_info() -> Dict[str, bool]: - return {"PROD": PROD} - - -app.include_router(asset_router, prefix="/assets", tags=["Assets"]) -app.include_router(auth_router, prefix="/auth", tags=["Auth"]) -app.include_router(dev_router, prefix="/dev", tags=["Dev"]) -app.include_router(user_router, prefix="/user", tags=["Users"]) -app.include_router(wrapped_router, prefix="/wrapped", tags=["Wrapped"]) diff --git a/frontend/backend/src/models/__init__.py b/frontend/backend/src/models/__init__.py deleted file mode 100644 index a9f4ca8c..00000000 --- a/frontend/backend/src/models/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -from src.models.user.contribs import ( - ContributionDay, - Language, - RepoContributionStats, - UserContributions, -) -from src.models.user.follows import User, UserFollows -from src.models.user.main import UserPackage -from src.models.wrapped.calendar import ( - CalendarData, - CalendarDayDatum, - CalendarLanguageDayDatum, -) -from src.models.wrapped.langs import LangData, LangDatum -from src.models.wrapped.main import WrappedPackage -from src.models.wrapped.numeric import ContribStats, LOCStats, MiscStats, NumericData -from src.models.wrapped.repos import RepoData, RepoDatum -from src.models.wrapped.time import DayData, MonthData, TimeDatum -from src.models.wrapped.timestamps import TimestampData, TimestampDatum - -__all__ = [ - "ContributionDay", - "Language", - "RepoContributionStats", - "UserContributions", - "User", - "UserFollows", - "UserPackage", - "CalendarData", - "CalendarDayDatum", - "CalendarLanguageDayDatum", - "LangData", - "LangDatum", - "WrappedPackage", - "ContribStats", - "LOCStats", - "MiscStats", - "NumericData", - "RepoData", - "RepoDatum", - "DayData", - "MonthData", - "TimeDatum", - "TimestampData", - "TimestampDatum", -] diff --git a/frontend/backend/src/models/background.py b/frontend/backend/src/models/background.py deleted file mode 100644 index 3676af09..00000000 --- a/frontend/backend/src/models/background.py +++ /dev/null @@ -1,12 +0,0 @@ -from datetime import date -from typing import Optional - -from pydantic import BaseModel - - -class UpdateUserBackgroundTask(BaseModel): - user_id: str - access_token: Optional[str] - private_access: bool - start_date: Optional[date] - end_date: Optional[date] diff --git a/frontend/backend/src/models/svg.py b/frontend/backend/src/models/svg.py deleted file mode 100644 index ee26a2a7..00000000 --- a/frontend/backend/src/models/svg.py +++ /dev/null @@ -1,23 +0,0 @@ -from typing import List, Optional - -from pydantic import BaseModel - - -class LanguageStats(BaseModel): - lang: str - loc: int - percent: float - color: Optional[str] - - -class RepoLanguage(BaseModel): - lang: str - color: Optional[str] - loc: int - - -class RepoStats(BaseModel): - repo: str - private: bool - langs: List[RepoLanguage] - loc: int diff --git a/frontend/backend/src/models/user/__init__.py b/frontend/backend/src/models/user/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/models/user/contribs.py b/frontend/backend/src/models/user/contribs.py deleted file mode 100644 index 6d56894b..00000000 --- a/frontend/backend/src/models/user/contribs.py +++ /dev/null @@ -1,310 +0,0 @@ -from datetime import date, datetime -from typing import Any, Dict, List, Optional, Tuple - -from pydantic import BaseModel - - -class Language(BaseModel): - color: Optional[str] - additions: int - deletions: int - - def compress(self) -> List[Any]: - return [self.color, self.additions, self.deletions] - - @classmethod - def decompress(cls, data: List[Any]) -> "Language": - return Language(color=data[0], additions=data[1], deletions=data[2]) - - def __add__(self, other: "Language") -> "Language": - return Language( - color=self.color, - additions=self.additions + other.additions, - deletions=self.deletions + other.deletions, - ) - - -class ContributionStats(BaseModel): - contribs_count: int - commits_count: int - issues_count: int - prs_count: int - reviews_count: int - repos_count: int - other_count: int - languages: Dict[str, Language] - - def compress(self) -> List[Any]: - out: List[Any] = [ - [ - self.contribs_count, - self.commits_count, - self.issues_count, - self.prs_count, - self.reviews_count, - self.repos_count, - self.other_count, - ], - *[[name] + stats.compress() for name, stats in self.languages.items()], - ] - - return out - - @classmethod - def decompress(cls, data: List[Any]) -> "ContributionStats": - return ContributionStats( - contribs_count=data[0][0], - commits_count=data[0][1], - issues_count=data[0][2], - prs_count=data[0][3], - reviews_count=data[0][4], - repos_count=data[0][5], - other_count=data[0][6], - languages={x[0]: Language.decompress(x[1:]) for x in data[1:]}, - ) - - def __add__(self, other: "ContributionStats") -> "ContributionStats": - languages = self.languages - for lang, lang_obj in other.languages.items(): - if lang in languages: - languages[lang] += lang_obj - else: - languages[lang] = lang_obj - - return ContributionStats( - contribs_count=self.contribs_count + other.contribs_count, - commits_count=self.commits_count + other.commits_count, - issues_count=self.issues_count + other.issues_count, - prs_count=self.prs_count + other.prs_count, - reviews_count=self.reviews_count + other.reviews_count, - repos_count=self.repos_count + other.repos_count, - other_count=self.other_count + other.other_count, - languages=languages, - ) - - @classmethod - def empty(cls) -> "ContributionStats": - return ContributionStats( - contribs_count=0, - commits_count=0, - issues_count=0, - prs_count=0, - reviews_count=0, - repos_count=0, - other_count=0, - languages={}, - ) - - -class ContributionLists(BaseModel): - commits: List[datetime] - issues: List[datetime] - prs: List[datetime] - reviews: List[datetime] - repos: List[datetime] - - def compress(self) -> List[Any]: - return [self.commits, self.issues, self.prs, self.reviews, self.repos] - - @classmethod - def decompress(cls, data: List[Any]) -> "ContributionLists": - return ContributionLists( - commits=data[0], issues=data[1], prs=data[2], reviews=data[3], repos=data[4] - ) - - -class ContributionDay(BaseModel): - date: str - weekday: int - stats: ContributionStats - lists: ContributionLists - - def compress(self) -> List[Any]: - return [ - self.date, - self.weekday, - self.stats.compress(), - self.lists.compress(), - ] - - @classmethod - def decompress(cls, data: List[Any]) -> "ContributionDay": - return ContributionDay( - date=data[0], - weekday=data[1], - stats=ContributionStats.decompress(data[2]), - lists=ContributionLists.decompress(data[3]), - ) - - -class RepoContributionStats(ContributionStats, BaseModel): - private: bool - contribs_count: int - commits_count: int - issues_count: int - prs_count: int - reviews_count: int - repos_count: int - other_count: int - languages: Dict[str, Language] - - def compress(self) -> List[Any]: - out = super().compress() - out[0].append(self.private) - return out - - @classmethod - def decompress(cls, data: List[Any]) -> "RepoContributionStats": - contribs = super().decompress(data).model_dump() - contribs["private"] = data[0][7] - return RepoContributionStats(**contribs) - - def __add__( # type: ignore - self, other: "RepoContributionStats" - ) -> "RepoContributionStats": - new_self = ContributionStats(**self.model_dump()) - new_other = ContributionStats(**other.model_dump()) - combined = (new_self + new_other).model_dump() - combined["private"] = self.private - return RepoContributionStats(**combined) - - -class UserContributions(BaseModel): - total_stats: ContributionStats - public_stats: ContributionStats - total: List[ContributionDay] - public: List[ContributionDay] - repo_stats: Dict[str, RepoContributionStats] - repos: Dict[str, List[ContributionDay]] - - def compress(self) -> List[Any]: - new_total_stats = self.total_stats.compress() - new_public_stats = self.public_stats.compress() - new_total = [x.compress() for x in self.total] - new_public = [x.compress() for x in self.public] - new_repo_stats = {k: v.compress() for k, v in self.repo_stats.items()} - new_repos = {k: [x.compress() for x in v] for k, v in self.repos.items()} - - return [ - new_total_stats, - new_public_stats, - new_total, - new_public, - new_repo_stats, - new_repos, - ] - - @classmethod - def decompress(cls, data: List[Any]) -> "UserContributions": - total_stats = ContributionStats.decompress(data[0]) - public_stats = ContributionStats.decompress(data[1]) - total = [ContributionDay.decompress(x) for x in data[2]] - public = [ContributionDay.decompress(x) for x in data[3]] - repo_stats = { - k: RepoContributionStats.decompress(v) for k, v in data[4].items() - } - repos = { - k: [ContributionDay.decompress(x) for x in v] for k, v in data[5].items() - } - - return UserContributions( - total_stats=total_stats, - public_stats=public_stats, - total=total, - public=public, - repo_stats=repo_stats, - repos=repos, - ) - - def __add__(self, other: "UserContributions") -> "UserContributions": - new_total_stats = self.total_stats + other.total_stats - new_public_stats = self.public_stats + other.public_stats - new_total = sorted(self.total + other.total, key=lambda x: x.date) - new_public = sorted(self.public + other.public, key=lambda x: x.date) - new_repo_stats = self.repo_stats - for repo, stats in other.repo_stats.items(): - if repo in new_repo_stats: - new_repo_stats[repo] += stats - else: - new_repo_stats[repo] = stats - new_repos = self.repos - for repo, days in other.repos.items(): - if repo in new_repos: - new_repos[repo] = sorted(new_repos[repo] + days, key=lambda x: x.date) - else: - new_repos[repo] = days - - return UserContributions( - total_stats=new_total_stats, - public_stats=new_public_stats, - total=new_total, - public=new_public, - repo_stats=new_repo_stats, - repos=new_repos, - ) - - @staticmethod - def trim_contribs( - contribs: List[ContributionDay], start_date: date, end_date: date - ) -> Tuple[List[ContributionDay], ContributionStats]: - new_total: List[ContributionDay] = [] - for day in contribs: - curr_date = datetime.strptime(day.date, "%Y-%m-%d").date() - if curr_date >= start_date and curr_date <= end_date: - new_total.append(day) - - new_languages: Dict[str, Language] = {} - for day in new_total: - for lang in day.stats.languages: - if lang in new_languages: - new_languages[lang] += day.stats.languages[lang] - else: - new_languages[lang] = day.stats.languages[lang] - - new_stats = ContributionStats( - contribs_count=sum(x.stats.contribs_count for x in new_total), - commits_count=sum(x.stats.commits_count for x in new_total), - issues_count=sum(x.stats.issues_count for x in new_total), - prs_count=sum(x.stats.prs_count for x in new_total), - reviews_count=sum(x.stats.reviews_count for x in new_total), - repos_count=sum(x.stats.repos_count for x in new_total), - other_count=sum(x.stats.other_count for x in new_total), - languages=new_languages, - ) - - return new_total, new_stats - - def trim(self, start: date, end: date) -> "UserContributions": - new_total, new_total_stats = self.trim_contribs(self.total, start, end) - new_public, new_public_stats = self.trim_contribs(self.public, start, end) - - new_repos_dict: Dict[str, List[ContributionDay]] = {} - new_repo_stats_dict: Dict[str, RepoContributionStats] = {} - for repo_name, repo in self.repos.items(): - new_repo_total, _new_repo_stats = self.trim_contribs(repo, start, end) - if len(new_repo_total) > 0: - new_repos_dict[repo_name] = new_repo_total - raw_new_repo_stats = _new_repo_stats.model_dump() - raw_new_repo_stats["private"] = self.repo_stats[repo_name].private - new_repo_stats = RepoContributionStats(**raw_new_repo_stats) - new_repo_stats_dict[repo_name] = new_repo_stats - - return UserContributions( - total_stats=new_total_stats, - public_stats=new_public_stats, - total=new_total, - public=new_public, - repo_stats=new_repo_stats_dict, - repos=new_repos_dict, - ) - - @classmethod - def empty(cls) -> "UserContributions": - return UserContributions( - total_stats=ContributionStats.empty(), - public_stats=ContributionStats.empty(), - total=[], - public=[], - repo_stats={}, - repos={}, - ) diff --git a/frontend/backend/src/models/user/follows.py b/frontend/backend/src/models/user/follows.py deleted file mode 100644 index ee878fa8..00000000 --- a/frontend/backend/src/models/user/follows.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import List, Optional - -from pydantic import BaseModel - - -class User(BaseModel): - name: Optional[str] - login: str - url: str - - class Config: - allow_none = True - - -class UserFollows(BaseModel): - followers: List[User] - following: List[User] diff --git a/frontend/backend/src/models/user/main.py b/frontend/backend/src/models/user/main.py deleted file mode 100644 index ed3a0b62..00000000 --- a/frontend/backend/src/models/user/main.py +++ /dev/null @@ -1,34 +0,0 @@ -from datetime import date -from typing import Any, Dict - -from pydantic import BaseModel - -from src.models.user.contribs import UserContributions - -# from src.models.user.follows import UserFollows - - -class UserPackage(BaseModel): - contribs: UserContributions - incomplete: bool = False - - def compress(self): - return { - "c": self.contribs.compress(), - } - - @classmethod - def decompress(cls, data: Dict[str, Any]) -> "UserPackage": - return UserPackage( - contribs=UserContributions.decompress(data["c"]), - ) - - def __add__(self, other: "UserPackage") -> "UserPackage": - return UserPackage(contribs=self.contribs + other.contribs) - - def trim(self, start: date, end: date) -> "UserPackage": - return UserPackage(contribs=self.contribs.trim(start, end)) - - @classmethod - def empty(cls) -> "UserPackage": - return UserPackage(contribs=UserContributions.empty()) diff --git a/frontend/backend/src/models/wrapped/__init__.py b/frontend/backend/src/models/wrapped/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/models/wrapped/calendar.py b/frontend/backend/src/models/wrapped/calendar.py deleted file mode 100644 index 5019561e..00000000 --- a/frontend/backend/src/models/wrapped/calendar.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Dict, List - -from pydantic import BaseModel - - -class CalendarLanguageDayDatum(BaseModel): - loc_added: int - loc_changed: int - - -class CalendarDayDatum(BaseModel): - day: str - contribs: int - commits: int - issues: int - prs: int - reviews: int - loc_added: int - loc_changed: int - top_langs: Dict[str, CalendarLanguageDayDatum] - - -class CalendarData(BaseModel): - days: List[CalendarDayDatum] - - @classmethod - def empty(cls) -> "CalendarData": - return CalendarData(days=[]) diff --git a/frontend/backend/src/models/wrapped/langs.py b/frontend/backend/src/models/wrapped/langs.py deleted file mode 100644 index e68abcfb..00000000 --- a/frontend/backend/src/models/wrapped/langs.py +++ /dev/null @@ -1,20 +0,0 @@ -from typing import List - -from pydantic import BaseModel - - -class LangDatum(BaseModel): - id: str - label: str - value: int - formatted_value: str - color: str - - -class LangData(BaseModel): - langs_changed: List[LangDatum] - langs_added: List[LangDatum] - - @classmethod - def empty(cls) -> "LangData": - return LangData(langs_changed=[], langs_added=[]) diff --git a/frontend/backend/src/models/wrapped/main.py b/frontend/backend/src/models/wrapped/main.py deleted file mode 100644 index 0fe759a4..00000000 --- a/frontend/backend/src/models/wrapped/main.py +++ /dev/null @@ -1,31 +0,0 @@ -from pydantic import BaseModel - -from src.models.wrapped.calendar import CalendarData -from src.models.wrapped.langs import LangData -from src.models.wrapped.numeric import NumericData -from src.models.wrapped.repos import RepoData -from src.models.wrapped.time import DayData, MonthData -from src.models.wrapped.timestamps import TimestampData - - -class WrappedPackage(BaseModel): - month_data: MonthData - day_data: DayData - calendar_data: CalendarData - numeric_data: NumericData - repo_data: RepoData - lang_data: LangData - timestamp_data: TimestampData - incomplete: bool = False - - @classmethod - def empty(cls) -> "WrappedPackage": - return WrappedPackage( - month_data=MonthData.empty(), - day_data=DayData.empty(), - calendar_data=CalendarData.empty(), - numeric_data=NumericData.empty(), - repo_data=RepoData.empty(), - lang_data=LangData.empty(), - timestamp_data=TimestampData.empty(), - ) diff --git a/frontend/backend/src/models/wrapped/numeric.py b/frontend/backend/src/models/wrapped/numeric.py deleted file mode 100644 index 3a3a0e04..00000000 --- a/frontend/backend/src/models/wrapped/numeric.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Optional, Tuple - -from pydantic import BaseModel - - -class ContribStats(BaseModel): - contribs: int - commits: int - issues: int - prs: int - reviews: int - other: int - - @classmethod - def empty(cls) -> "ContribStats": - return ContribStats( - contribs=0, - commits=0, - issues=0, - prs=0, - reviews=0, - other=0, - ) - - -class MiscStats(BaseModel): - total_days: int - longest_streak: int - longest_streak_days: Tuple[int, int, str, str] - longest_gap: int - longest_gap_days: Tuple[int, int, str, str] - weekend_percent: int - best_day_count: int - best_day_date: Optional[str] - best_day_index: Optional[int] - - @classmethod - def empty(cls) -> "MiscStats": - return MiscStats( - total_days=0, - longest_streak=0, - longest_streak_days=(0, 0, "", ""), - longest_gap=0, - longest_gap_days=(0, 0, "", ""), - weekend_percent=0, - best_day_count=0, - best_day_date=None, - best_day_index=None, - ) - - -class LOCStats(BaseModel): - loc_additions: str - loc_deletions: str - loc_changed: str - loc_added: str - loc_additions_per_commit: int - loc_deletions_per_commit: int - loc_changed_per_day: int - - @classmethod - def empty(cls) -> "LOCStats": - return LOCStats( - loc_additions="0", - loc_deletions="0", - loc_changed="0", - loc_added="0", - loc_additions_per_commit=0, - loc_deletions_per_commit=0, - loc_changed_per_day=0, - ) - - -class NumericData(BaseModel): - contribs: ContribStats - misc: MiscStats - loc: LOCStats - - @classmethod - def empty(cls) -> "NumericData": - return NumericData( - contribs=ContribStats.empty(), - misc=MiscStats.empty(), - loc=LOCStats.empty(), - ) diff --git a/frontend/backend/src/models/wrapped/repos.py b/frontend/backend/src/models/wrapped/repos.py deleted file mode 100644 index ab89abd1..00000000 --- a/frontend/backend/src/models/wrapped/repos.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import List - -from pydantic import BaseModel - - -class RepoDatum(BaseModel): - id: int - label: str - value: int - formatted_value: str - - -class RepoData(BaseModel): - repos_changed: List[RepoDatum] - repos_added: List[RepoDatum] - - @classmethod - def empty(cls) -> "RepoData": - return RepoData(repos_changed=[], repos_added=[]) diff --git a/frontend/backend/src/models/wrapped/time.py b/frontend/backend/src/models/wrapped/time.py deleted file mode 100644 index 6e863119..00000000 --- a/frontend/backend/src/models/wrapped/time.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import List - -from pydantic import BaseModel - - -class TimeDatum(BaseModel): - index: int - contribs: int - loc_changed: int - formatted_loc_changed: str - - -class MonthData(BaseModel): - months: List[TimeDatum] - - @classmethod - def empty(cls) -> "MonthData": - return MonthData(months=[]) - - -class DayData(BaseModel): - days: List[TimeDatum] - - @classmethod - def empty(cls) -> "DayData": - return DayData(days=[]) diff --git a/frontend/backend/src/models/wrapped/timestamps.py b/frontend/backend/src/models/wrapped/timestamps.py deleted file mode 100644 index 8f9f44c7..00000000 --- a/frontend/backend/src/models/wrapped/timestamps.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import List - -from pydantic import BaseModel - - -class TimestampDatum(BaseModel): - type: str - weekday: int - timestamp: int - - -class TimestampData(BaseModel): - contribs: List[TimestampDatum] - - @classmethod - def empty(cls) -> "TimestampData": - return TimestampData(contribs=[]) diff --git a/frontend/backend/src/processing/auth.py b/frontend/backend/src/processing/auth.py deleted file mode 100644 index e2e88143..00000000 --- a/frontend/backend/src/processing/auth.py +++ /dev/null @@ -1,67 +0,0 @@ -from typing import Any, Dict, Optional, Tuple - -from src.data.github.auth import authenticate as github_authenticate -from src.data.mongo.user import ( - PublicUserModel, - delete_user as db_delete_user, - get_public_user as db_get_public_user, - update_user as db_update_user, -) -from src.models.background import UpdateUserBackgroundTask - -# frontend first calls set_user_key with code and user_key -# next they call authenticate which determines the user_id to associate with the code/user_key -# these actions should happen sequentially, so in-memory storage is fine -code_key_map: Dict[str, str] = {} - - -async def set_user_key(code: str, user_key: str) -> str: - code_key_map[code] = user_key - return user_key - - -async def authenticate( - code: str, private_access: bool -) -> Tuple[str, Optional[UpdateUserBackgroundTask]]: - user_id, access_token = await github_authenticate(code) - - curr_user: Optional[PublicUserModel] = await db_get_public_user(user_id) - - raw_user: Dict[str, Any] = { - "user_id": user_id, - "access_token": access_token, - "user_key": code_key_map.get(code, None), - "private_access": private_access, - } - - background_task = None - if curr_user is not None: - curr_private_access = curr_user.private_access - new_private_access = curr_private_access or private_access - raw_user["private_access"] = new_private_access - - if new_private_access != curr_private_access: - # new private access status - background_task = UpdateUserBackgroundTask( - user_id=user_id, - access_token=access_token, - private_access=new_private_access, - start_date=None, - end_date=None, - ) - else: - # first time sign up - background_task = UpdateUserBackgroundTask( - user_id=user_id, - access_token=access_token, - private_access=private_access, - start_date=None, - end_date=None, - ) - - # await db_update_user(user_id, raw_user) - return user_id, background_task - - -async def delete_user(user_id: str, user_key: str, use_user_key: bool = True) -> bool: - return await db_delete_user(user_id, user_key, use_user_key) diff --git a/frontend/backend/src/processing/user/__init__.py b/frontend/backend/src/processing/user/__init__.py deleted file mode 100644 index c34d1792..00000000 --- a/frontend/backend/src/processing/user/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from src.processing.user.commits import get_top_languages, get_top_repos -from src.processing.user.svg import svg_base - -__all__ = ["get_top_languages", "get_top_repos", "svg_base"] diff --git a/frontend/backend/src/processing/user/commits.py b/frontend/backend/src/processing/user/commits.py deleted file mode 100644 index 38f90470..00000000 --- a/frontend/backend/src/processing/user/commits.py +++ /dev/null @@ -1,136 +0,0 @@ -from typing import Any, Dict, List, Optional, Tuple, Union - -from src.constants import DEFAULT_COLOR -from src.models import UserPackage -from src.models.svg import LanguageStats, RepoStats - -dict_type = Dict[str, Union[str, int, float]] - - -def loc_metric_func(loc_metric: str, additions: int, deletions: int) -> int: - if loc_metric == "changed": - return additions + deletions - return additions - deletions - - -def get_top_languages( - data: UserPackage, loc_metric: str, include_private: bool -) -> Tuple[List[LanguageStats], int]: - raw_languages = ( - data.contribs.total_stats.languages - if include_private - else data.contribs.public_stats.languages - ) - - languages_list = [ - LanguageStats( - lang=lang, - color=stats.color or DEFAULT_COLOR, - loc=loc_metric_func(loc_metric, stats.additions, stats.deletions), - percent=-1, - ) - for lang, stats in raw_languages.items() - ] - - languages_list = list(filter(lambda x: x.loc > 0, languages_list)) - - total_loc = sum(x.loc for x in languages_list) + 1 - total = LanguageStats(lang="Total", color=None, loc=total_loc, percent=100) - - languages_list = sorted(languages_list, key=lambda x: x.loc, reverse=True) - other = LanguageStats(lang="Other", color="#ededed", loc=0, percent=-1) - for language in languages_list[4:]: - other.loc = other.loc + language.loc - - languages_list = [total] + languages_list[:4] + [other] - - new_languages_list: List[LanguageStats] = [] - for lang in languages_list: - lang.percent = float(round(100 * lang.loc / total_loc, 2)) - if lang.percent > 1: # 1% minimum to show - new_languages_list.append(LanguageStats.model_validate(lang)) - - commits_excluded = data.contribs.public_stats.other_count - if include_private: - commits_excluded = data.contribs.total_stats.other_count - - return new_languages_list, commits_excluded - - -def get_top_repos( - data: UserPackage, loc_metric: str, include_private: bool, group: str -) -> Tuple[List[RepoStats], int]: - repos: List[Any] = [ - { - "repo": repo, - "private": repo_stats.private, - "langs": [ - { - "lang": x[0], - "color": x[1].color, - "loc": loc_metric_func(loc_metric, x[1].additions, x[1].deletions), - } - for x in list(repo_stats.languages.items()) - ], - } - for repo, repo_stats in data.contribs.repo_stats.items() - if include_private or not repo_stats.private - ] - - for repo in repos: - repo["loc"] = sum(x["loc"] for x in repo["langs"]) # first estimate - repos = list(filter(lambda x: x["loc"] > 0, repos)) - - for repo in repos: - repo["langs"] = [x for x in repo["langs"] if x["loc"] > 0.05 * repo["loc"]] - repo["loc"] = sum(x["loc"] for x in repo["langs"]) # final estimate - repos = sorted(repos, key=lambda x: x["loc"], reverse=True) - - new_repos = [ - RepoStats.model_validate(x) for x in repos if x["loc"] > 0.01 * repos[0]["loc"] - ] - - commits_excluded = data.contribs.public_stats.other_count - if include_private: - commits_excluded = data.contribs.total_stats.other_count - - # With n bars, group from n onwards into the last bar - bars = 4 # TODO: make this configurable (see issues) - - if group == "none" or len(new_repos) <= bars: - return new_repos[:bars], commits_excluded - - out_repos = [] - other_repos = [] - if group == "other": - out_repos = new_repos[: bars - 1] - other_repos = new_repos[bars - 1 :] - elif group == "private": - public_repos = [x for x in new_repos if not x.private] - private_repos = [x for x in new_repos if x.private] - if len(public_repos) < 4 and len(private_repos) > 0: - public_repos += private_repos[: bars - len(public_repos) - 1] - private_repos = private_repos[bars - len(public_repos) - 1 :] - out_repos = sorted(public_repos[: bars - 1], key=lambda x: x.loc, reverse=True) - other_repos = public_repos[bars - 1 :] + private_repos - else: - raise ValueError("Invalid group value") - - other: Dict[str, Tuple[int, Optional[str]]] = {} - for repo in other_repos: - for _lang in repo.langs: - lang = _lang.lang - if lang not in other: - other[lang] = (0, _lang.color) - other[lang] = (other[lang][0] + _lang.loc, other[lang][1]) - - out_repos.append( - RepoStats( - repo="other/repos", - private=False, - langs=[{"lang": k, "loc": v[0], "color": v[1]} for k, v in other.items()], # type: ignore - loc=sum(v[0] for v in other.values()), - ) - ) - - return out_repos, commits_excluded diff --git a/frontend/backend/src/processing/user/svg.py b/frontend/backend/src/processing/user/svg.py deleted file mode 100644 index 6a3ee8af..00000000 --- a/frontend/backend/src/processing/user/svg.py +++ /dev/null @@ -1,32 +0,0 @@ -from datetime import date -from typing import Optional, Tuple - -from src.aggregation.layer2.user import get_user, get_user_demo -from src.models import UserPackage -from src.models.background import UpdateUserBackgroundTask -from src.utils import use_time_range - - -async def svg_base( - user_id: str, - start_date: date, - end_date: date, - time_range: str, - demo: bool, - no_cache: bool = False, -) -> Tuple[Optional[UserPackage], bool, Optional[UpdateUserBackgroundTask], str]: - # process time_range, start_date, end_date - time_range = "one_month" if demo else time_range - start_date, end_date, time_str = use_time_range(time_range, start_date, end_date) - complete = True # overwritten later if not complete - background_task = None - - # fetch data, either using demo or user method - if demo: - output = await get_user_demo(user_id, start_date, end_date, no_cache=no_cache) - else: - output, complete, background_task = await get_user( - user_id, start_date, end_date, no_cache=no_cache - ) - - return output, complete, background_task, time_str diff --git a/frontend/backend/src/processing/wrapped/__init__.py b/frontend/backend/src/processing/wrapped/__init__.py deleted file mode 100644 index a04782ee..00000000 --- a/frontend/backend/src/processing/wrapped/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from src.processing.wrapped.main import query_wrapped_user - -__all__ = ["query_wrapped_user"] diff --git a/frontend/backend/src/processing/wrapped/calendar.py b/frontend/backend/src/processing/wrapped/calendar.py deleted file mode 100644 index a0905a7a..00000000 --- a/frontend/backend/src/processing/wrapped/calendar.py +++ /dev/null @@ -1,51 +0,0 @@ -from datetime import datetime, timedelta -from typing import Any, Dict, List - -from src.models import CalendarData, CalendarDayDatum, UserPackage - - -def get_calendar_data(data: UserPackage, year: int) -> CalendarData: - top_langs = [ - x[0] - for x in sorted( - data.contribs.total_stats.languages.items(), - key=lambda x: x[1].additions + x[1].deletions, - reverse=True, - )[:5] - ] - - total_out: List[CalendarDayDatum] = [] - items_dict = {item.date: item for item in data.contribs.total} - for i in range(365): - date = (datetime(year, 1, 1) + timedelta(days=i - 1)).strftime("%Y-%m-%d") - item = items_dict.get(date) - out: Dict[str, Any] = { - "day": date, - "contribs": 0, - "commits": 0, - "issues": 0, - "prs": 0, - "reviews": 0, - "loc_added": 0, - "loc_changed": 0, - "top_langs": {k: {"loc_added": 0, "loc_changed": 0} for k in top_langs}, - } - - if item is not None: - out["contribs"] = item.stats.contribs_count - out["commits"] = item.stats.commits_count - out["issues"] = item.stats.issues_count - out["prs"] = item.stats.prs_count - out["reviews"] = item.stats.reviews_count - - for k, v in item.stats.languages.items(): - if k in top_langs: - out["top_langs"][k]["loc_added"] = v.additions - v.deletions - out["top_langs"][k]["loc_changed"] = v.additions + v.deletions - out["loc_added"] += v.additions - v.deletions - out["loc_changed"] += v.additions + v.deletions - - out_obj = CalendarDayDatum.model_validate(out) - total_out.append(out_obj) - - return CalendarData.model_validate({"days": total_out}) diff --git a/frontend/backend/src/processing/wrapped/langs.py b/frontend/backend/src/processing/wrapped/langs.py deleted file mode 100644 index d07aeb55..00000000 --- a/frontend/backend/src/processing/wrapped/langs.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import List - -from src.constants import DEFAULT_COLOR -from src.models import LangData, LangDatum, Language, UserPackage -from src.utils import format_number - - -def _count_loc(x: Language, metric: str) -> int: - if metric == "changed": - return x.additions + x.deletions - return x.additions - x.deletions - - -def get_lang_data(data: UserPackage) -> LangData: - out = {} - for m in ["changed", "added"]: - langs = sorted( - data.contribs.total_stats.languages.items(), - key=lambda x: _count_loc(x[1], m), - reverse=True, - ) - lang_objs: List[LangDatum] = [] - for k, v in list(langs)[:5]: - lang_data = { - "id": k, - "label": k, - "value": _count_loc(v, m), - "formatted_value": format_number(_count_loc(v, m)), - "color": v.color, - } - lang_objs.append(LangDatum.model_validate(lang_data)) - - # remaining languages - total_count = sum(_count_loc(v, m) for _, v in list(langs)[5:]) - lang_data = { - "id": "other", - "label": "other", - "value": total_count, - "formatted_value": format_number(total_count), - "color": DEFAULT_COLOR, - } - if total_count > 100: - lang_objs.append(LangDatum.model_validate(lang_data)) - - out[f"langs_{m}"] = lang_objs - - return LangData.model_validate(out) diff --git a/frontend/backend/src/processing/wrapped/main.py b/frontend/backend/src/processing/wrapped/main.py deleted file mode 100644 index 1fb30464..00000000 --- a/frontend/backend/src/processing/wrapped/main.py +++ /dev/null @@ -1,34 +0,0 @@ -from datetime import date, timedelta -from typing import Optional, Tuple - -from src.aggregation.layer1 import query_user -from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user -from src.models import UserPackage, WrappedPackage -from src.processing.wrapped.package import get_wrapped_data -from src.utils import alru_cache - - -@alru_cache(ttl=timedelta(hours=12)) -async def query_wrapped_user( - user_id: str, year: int, no_cache: bool = False -) -> Tuple[bool, Optional[WrappedPackage]]: - start_date, end_date = date(year, 1, 1), date(year, 12, 31) - user: Optional[PublicUserModel] = await db_get_public_user(user_id) - private_access = False - access_token = None - if user is not None and user.private_access: - private_access = True - access_token = user.access_token - user_package: UserPackage = await query_user( - user_id, - access_token, - private_access, - start_date, - end_date, - max_time=40, - no_cache=True, - ) - wrapped_package = get_wrapped_data(user_package, year) - - # Don't cache if incomplete - return (not wrapped_package.incomplete, wrapped_package) diff --git a/frontend/backend/src/processing/wrapped/numeric.py b/frontend/backend/src/processing/wrapped/numeric.py deleted file mode 100644 index 5d9e374b..00000000 --- a/frontend/backend/src/processing/wrapped/numeric.py +++ /dev/null @@ -1,129 +0,0 @@ -from collections import defaultdict -from datetime import datetime -from typing import Dict - -from src.models import ContribStats, LOCStats, MiscStats, NumericData, UserPackage - - -def get_contrib_stats(data: UserPackage) -> ContribStats: - return ContribStats.model_validate( - { - "contribs": data.contribs.total_stats.contribs_count, - "commits": data.contribs.total_stats.commits_count, - "issues": data.contribs.total_stats.issues_count, - "prs": data.contribs.total_stats.prs_count, - "reviews": data.contribs.total_stats.reviews_count, - "other": data.contribs.total_stats.other_count, - } - ) - - -def get_misc_stats(data: UserPackage, year: int) -> MiscStats: - weekdays: Dict[int, int] = defaultdict(int) - yeardays, distinct_days, total_contribs = {}, 0, 0 - for item in data.contribs.total: - count = item.stats.contribs_count - weekdays[item.weekday] += count - total_contribs += item.stats.contribs_count - if count > 0: - date = datetime.fromisoformat(item.date) - yeardays[date.timetuple().tm_yday - 1] = 1 - distinct_days += 1 - curr, best, best_dates = 0, 0, (1, 1) - for i in range(366): - curr = curr + 1 if i in yeardays else 0 - if curr > best: - best = curr - best_dates = (i - curr + 2, i + 1) - longest_streak = max(best, curr) - longest_streak_days = ( - best_dates[0], - best_dates[1], - datetime.fromordinal(max(1, best_dates[0])).strftime("%b %d"), - datetime.fromordinal(max(1, best_dates[1])).strftime("%b %d"), - ) - curr, best, best_dates = 0, 0, (1, 1) - days = (datetime.now() - datetime(year, 1, 1)).days - for i in range(min(days, 365)): - curr = 0 if i in yeardays else curr + 1 - if curr > best: - best = curr - best_dates = (i - curr + 2, i + 1) - longest_gap = max(best, curr) - longest_gap_days = ( - best_dates[0], - best_dates[1], - datetime.fromordinal(max(1, best_dates[0])).strftime("%b %d"), - datetime.fromordinal(max(1, best_dates[1])).strftime("%b %d"), - ) - weekend_percent = (weekdays[0] + weekdays[6]) / max(1, total_contribs) - - best_day_count, best_day_date, best_day_index = 0, None, None - if len(data.contribs.total) > 0: - best_day = max(data.contribs.total, key=lambda x: x.stats.contribs_count) - best_day_index = datetime.fromisoformat(best_day.date).timetuple().tm_yday - best_day_count = best_day.stats.contribs_count - best_day_date = best_day.date - - return MiscStats.model_validate( - { - "total_days": distinct_days, - "longest_streak": longest_streak, - "longest_streak_days": longest_streak_days, - "longest_gap": longest_gap, - "longest_gap_days": longest_gap_days, - "weekend_percent": round(100 * weekend_percent), - "best_day_count": best_day_count, - "best_day_date": best_day_date, - "best_day_index": best_day_index, - } - ) - - -def format_loc_number(number: int) -> str: - if number < 1e3: - return str(100 * round(number / 100)) - if number < 1e6: - return f"{str(round(number / 1000.0))},000" - return f"{str(round(number / 1000000.0))},000,000" - - -def get_loc_stats(data: UserPackage) -> LOCStats: - dataset = data.contribs.total_stats.languages.values() - return LOCStats.model_validate( - { - "loc_additions": format_loc_number(sum(x.additions for x in dataset)), - "loc_deletions": format_loc_number(sum(x.deletions for x in dataset)), - "loc_changed": format_loc_number( - sum(x.additions + x.deletions for x in dataset) - ), - "loc_added": format_loc_number( - sum(x.additions - x.deletions for x in dataset) - ), - "loc_additions_per_commit": round( - ( - sum(x.additions for x in dataset) - / max(1, data.contribs.total_stats.commits_count) - ) - ), - "loc_deletions_per_commit": round( - ( - sum(x.deletions for x in dataset) - / max(1, data.contribs.total_stats.commits_count) - ) - ), - "loc_changed_per_day": round( - sum(x.additions + x.deletions for x in dataset) / 365 - ), - } - ) - - -def get_numeric_data(data: UserPackage, year: int) -> NumericData: - return NumericData.model_validate( - { - "contribs": get_contrib_stats(data), - "misc": get_misc_stats(data, year), - "loc": get_loc_stats(data), - } - ) diff --git a/frontend/backend/src/processing/wrapped/package.py b/frontend/backend/src/processing/wrapped/package.py deleted file mode 100644 index 8d451b27..00000000 --- a/frontend/backend/src/processing/wrapped/package.py +++ /dev/null @@ -1,32 +0,0 @@ -from src.models import UserPackage, WrappedPackage -from src.processing.wrapped.calendar import get_calendar_data -from src.processing.wrapped.langs import get_lang_data -from src.processing.wrapped.numeric import get_numeric_data -from src.processing.wrapped.repos import get_repo_data -from src.processing.wrapped.time import get_day_data, get_month_data -from src.processing.wrapped.timestamps import get_timestamp_data - -# from src.processing.user.follows import get_user_follows - - -def get_wrapped_data(user_package: UserPackage, year: int) -> WrappedPackage: - """packages all processing steps for the user query""" - - month_data = get_month_data(user_package) - day_data = get_day_data(user_package) - calendar_data = get_calendar_data(user_package, year) - numeric_data = get_numeric_data(user_package, year) - repo_data = get_repo_data(user_package) - lang_data = get_lang_data(user_package) - timestamp_data = get_timestamp_data(user_package) - - return WrappedPackage( - month_data=month_data, - day_data=day_data, - calendar_data=calendar_data, - numeric_data=numeric_data, - repo_data=repo_data, - lang_data=lang_data, - timestamp_data=timestamp_data, - incomplete=user_package.incomplete, - ) diff --git a/frontend/backend/src/processing/wrapped/repos.py b/frontend/backend/src/processing/wrapped/repos.py deleted file mode 100644 index 0bff49a5..00000000 --- a/frontend/backend/src/processing/wrapped/repos.py +++ /dev/null @@ -1,50 +0,0 @@ -from typing import List - -from src.models import Language, RepoContributionStats, RepoData, RepoDatum, UserPackage -from src.utils import format_number - - -def _count_loc(x: Language, metric: str) -> int: - if metric == "changed": - return x.additions + x.deletions - return x.additions - x.deletions - - -def _count_repo_loc(x: RepoContributionStats, metric: str) -> int: - return sum(_count_loc(lang, metric) for lang in x.languages.values()) - - -def get_repo_data(data: UserPackage) -> RepoData: - out = {} - for m in ["changed", "added"]: - repos = sorted( - data.contribs.repo_stats.items(), - key=lambda x: _count_repo_loc(x[1], m), - reverse=True, - ) - repo_objs: List[RepoDatum] = [] - - # first five repositories - for i, (k, v) in enumerate(list(repos)[:5]): - repo_data = { - "id": i, - "label": "private/repository" if v.private else k, - "value": _count_repo_loc(v, m), - "formatted_value": format_number(_count_repo_loc(v, m)), - } - repo_objs.append(RepoDatum.model_validate(repo_data)) - - # remaining repositories - total_count = sum(_count_repo_loc(v, m) for _, v in list(repos)[5:]) - repo_data = { - "id": -1, - "label": "other", - "value": total_count, - "formatted_value": format_number(total_count), - } - if total_count > 100: - repo_objs.append(RepoDatum.model_validate(repo_data)) - - out[f"repos_{m}"] = repo_objs - - return RepoData.model_validate(out) diff --git a/frontend/backend/src/processing/wrapped/time.py b/frontend/backend/src/processing/wrapped/time.py deleted file mode 100644 index c5b8df6a..00000000 --- a/frontend/backend/src/processing/wrapped/time.py +++ /dev/null @@ -1,62 +0,0 @@ -from collections import defaultdict -from datetime import datetime -from typing import Dict, List, Union - -from src.models import DayData, MonthData, TimeDatum, UserPackage -from src.utils import format_number - - -def get_month_data(data: UserPackage) -> MonthData: - months: Dict[int, Dict[str, int]] = defaultdict( - lambda: {"contribs": 0, "loc_changed": 0} - ) - - for item in data.contribs.total: - month = datetime.fromisoformat(item.date).month - 1 - months[month]["contribs"] += item.stats.contribs_count - loc_changed = sum( - x.additions + x.deletions for x in item.stats.languages.values() - ) - - months[month]["loc_changed"] += loc_changed - - out: List[TimeDatum] = [] - for k in range(12): - v = months[k] - _obj: Dict[str, Union[str, int]] = { - "index": k, - **v, - "formatted_loc_changed": format_number(v["loc_changed"]), - } - - out.append(TimeDatum.model_validate(_obj)) - - return MonthData(months=out) - - -def get_day_data(data: UserPackage) -> DayData: - days: Dict[int, Dict[str, int]] = defaultdict( - lambda: {"contribs": 0, "loc_changed": 0} - ) - - for item in data.contribs.total: - day = (datetime.fromisoformat(item.date).weekday() + 1) % 7 - days[day]["contribs"] += item.stats.contribs_count - loc_changed = sum( - x.additions + x.deletions for x in item.stats.languages.values() - ) - - days[day]["loc_changed"] += loc_changed - - out: List[TimeDatum] = [] - for k in range(7): - v = days[k] - _obj: Dict[str, Union[str, int]] = { - "index": k, - **v, - "formatted_loc_changed": format_number(v["loc_changed"]), - } - - out.append(TimeDatum.model_validate(_obj)) - - return DayData(days=out) diff --git a/frontend/backend/src/processing/wrapped/timestamps.py b/frontend/backend/src/processing/wrapped/timestamps.py deleted file mode 100644 index cf338ff4..00000000 --- a/frontend/backend/src/processing/wrapped/timestamps.py +++ /dev/null @@ -1,33 +0,0 @@ -from datetime import datetime -from random import shuffle -from typing import Any, List - -from src.models import TimestampData, TimestampDatum, UserPackage - -MAX_ITEMS = 200 - - -def date_to_seconds_since_midnight(date: datetime) -> int: - return (date.hour * 60 * 60) + (date.minute * 60) + date.second - - -def get_timestamp_data(data: UserPackage) -> TimestampData: - out: List[Any] = [] - for item in data.contribs.total: - lists = item.lists - lists = [lists.commits, lists.issues, lists.prs, lists.reviews] - for type, list in zip(["commit", "issue", "pr", "review"], lists): - out.extend( - { - "type": type, - "weekday": item.weekday, - "timestamp": date_to_seconds_since_midnight(obj), - } - for obj in list - ) - - shuffle(out) - out = out[:MAX_ITEMS] - out = [TimestampDatum.model_validate(x) for x in out] - - return TimestampData(contribs=out) diff --git a/frontend/backend/src/render/__init__.py b/frontend/backend/src/render/__init__.py deleted file mode 100644 index ad179f3a..00000000 --- a/frontend/backend/src/render/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from src.render.error import ( - get_empty_demo_svg, - get_error_svg, - get_loading_svg, - get_no_data_svg, -) -from src.render.top_langs import get_top_langs_svg -from src.render.top_repos import get_top_repos_svg - -__all__ = [ - "get_empty_demo_svg", - "get_error_svg", - "get_loading_svg", - "get_no_data_svg", - "get_top_langs_svg", - "get_top_repos_svg", -] diff --git a/frontend/backend/src/render/error.py b/frontend/backend/src/render/error.py deleted file mode 100644 index 03b0b86d..00000000 --- a/frontend/backend/src/render/error.py +++ /dev/null @@ -1,127 +0,0 @@ -# type: ignore - -from svgwrite import Drawing - -from src.constants import BACKEND_URL -from src.render.style import styles_no_animation, themes -from src.render.template import get_template - -THEME = "classic" - - -def get_error_svg() -> Drawing: - d = Drawing(size=(300, 285)) - d.defs.add(d.style(styles_no_animation[THEME])) - - d.add( - d.rect( - size=(299, 284), - insert=(0.5, 0.5), - rx=4.5, - stroke=themes[THEME]["border_color"], - fill=themes[THEME]["bg_color"], - ) - ) - - d.add(d.text("Unknown Error", insert=(25, 35), class_=f"{THEME}-header")) - - d.add( - d.text( - "Please try again later or raise a ticket on GitHub", - insert=(25, 60), - class_=f"{THEME}-lang-name", - ) - ) - - d.add( - d.image(f"{BACKEND_URL}/assets/error", insert=(85, 100), style="opacity: 50%") - ) - - return d - - -def get_empty_demo_svg(header: str) -> Drawing: - d = Drawing(size=(300, 285)) - d.defs.add(d.style(styles_no_animation[THEME])) - - d.add( - d.rect( - size=(299, 284), - insert=(0.5, 0.5), - rx=4.5, - stroke=themes[THEME]["border_color"], - fill=themes[THEME]["bg_color"], - ) - ) - - d.add(d.text(header, insert=(25, 35), class_=f"{THEME}-header")) - - d.add( - d.text( - "Enter your username to start!", - insert=(25, 60), - class_=f"{THEME}-lang-name", - ) - ) - - d.add( - d.image( - f"{BACKEND_URL}/assets/stopwatch", insert=(85, 100), style="opacity: 50%" - ) - ) - - return d - - -def get_loading_svg() -> Drawing: - d = Drawing(size=(300, 285)) - d.defs.add(d.style(styles_no_animation[THEME])) - - d.add( - d.rect( - size=(299, 284), - insert=(0.5, 0.5), - rx=4.5, - stroke=themes[THEME]["border_color"], - fill=themes[THEME]["bg_color"], - ) - ) - - d.add( - d.text("Loading data, hang tight!", insert=(25, 35), class_=f"{THEME}-header") - ) - - d.add( - d.text( - "Please wait a couple seconds and refresh the page.", - insert=(25, 60), - class_=f"{THEME}-lang-name", - ) - ) - - d.add( - d.image( - f"{BACKEND_URL}/assets/stopwatch", insert=(85, 100), style="opacity: 50%" - ) - ) - - return d - - -def get_no_data_svg(header: str, subheader: str) -> Drawing: - d, dp = get_template( - width=300, - height=285, - padding=20, - header_text=header, - subheader_text=subheader, - debug=False, - theme=THEME, - ) - - d.add(d.image(f"{BACKEND_URL}/assets/error", insert=(85, 80), style="opacity: 50%")) - - dp.add(d.text("No data to show", insert=(55, 220), class_=f"{THEME}-image-text")) - - d.add(dp) - return d diff --git a/frontend/backend/src/render/style.py b/frontend/backend/src/render/style.py deleted file mode 100644 index e4777972..00000000 --- a/frontend/backend/src/render/style.py +++ /dev/null @@ -1,116 +0,0 @@ -from typing import List, Tuple - -themes = { - "classic": { - "header_color": "#2f80ed", - "subheader_color": "#666", - "text_color": "#333", - "bg_color": "#fffefe", - "border_color": "#e4e2e2", - "bar_color": "#ddd", - }, - "dark": { - "header_color": "#fff", - "subheader_color": "#9f9f9f", - "text_color": "#9f9f9f", - "bg_color": "#151515", - "border_color": "#e4e2e2", - "bar_color": "#333", - }, - "bright_lights": { - "header_color": "#fff", - "subheader_color": "#0e86d4", - "text_color": "#b1d4e0", - "bg_color": "#003060", - "border_color": "#0e86d4", - "bar_color": "#ddd", - }, - "rosettes": { - "header_color": "#fff", - "subheader_color": "#b6e2d3", - "text_color": "#fae8e0", - "bg_color": "#ef7c8e", - "border_color": "#b6e2d3", - "bar_color": "#ddd", - }, - "ferns": { - "header_color": "#116530", - "subheader_color": "#18a558", - "text_color": "#116530", - "bg_color": "#a3ebb1", - "border_color": "#21b6a8", - "bar_color": "#ddd", - }, - "synthwaves": { - "header_color": "#e2e9ec", - "subheader_color": "#e5289e", - "text_color": "#ef8539", - "bg_color": "#2b213a", - "border_color": "#e5289e", - "bar_color": "#ddd", - }, -} - - -def get_style(theme: str = "classic", use_animation: bool = True) -> str: - # List[Tuple["selector", List[Tuple["property", "is_animation"]], "is_animation"]] - _style: List[Tuple[str, List[Tuple[str, bool]], bool]] = [ - ( - ".header", - [ - ("font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;", False), - ("fill: " + themes[theme]["header_color"] + ";", False), - ("animation: fadeInAnimation 0.8s ease-in-out forwards;", True), - ], - False, - ), - ( - ".subheader", - [ - ("font: 500 10px 'Segoe UI', Ubuntu, Sans-Serif;", False), - ("fill: " + themes[theme]["subheader_color"] + ";", False), - ("animation: fadeInAnimation 0.8s ease-in-out forwards;", True), - ], - False, - ), - ( - ".lang-name", - [ - ("font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif;", False), - ("fill: " + themes[theme]["text_color"] + ";", False), - ], - False, - ), - ( - ".image-text", - [ - ("font: 500 20px 'Segoe UI', Ubuntu, Sans-Serif;", False), - ("fill: " + themes[theme]["text_color"] + ";", False), - ("opacity: 50%;", False), - ], - False, - ), - ( - "@keyframes fadeInAnimation", - [("from { opacity: 0; } to { opacity: 1; }", True)], - True, - ), - ] - - return "\n".join( - [ - ( - (rule[0].replace(".", f".{theme}-") + " {") - + "\n".join( - item[0] for item in rule[1] if (use_animation or not item[1]) - ) - + "}" - ) - for rule in _style - if use_animation or not rule[2] - ] - ) - - -styles = {k: get_style(k) for k in themes.keys()} -styles_no_animation = {k: get_style(k, False) for k in themes.keys()} diff --git a/frontend/backend/src/render/template.py b/frontend/backend/src/render/template.py deleted file mode 100644 index 7b5ab1d9..00000000 --- a/frontend/backend/src/render/template.py +++ /dev/null @@ -1,126 +0,0 @@ -# type: ignore - -from typing import List, Tuple - -from svgwrite import Drawing -from svgwrite.container import Group -from svgwrite.shapes import Circle - -from src.constants import DEFAULT_COLOR -from src.render.style import styles, styles_no_animation, themes - - -def get_template( - width: int, - height: int, - padding: int, - header_text: str, - subheader_text: str, - theme: str, - use_animation: bool = True, - debug: bool = False, -) -> Tuple[Drawing, Group]: - d = Drawing(size=(width, height)) - - style = styles[theme] - style_no_animation = styles_no_animation[theme] - d.defs.add(d.style(style if use_animation else style_no_animation)) - - d.add( - d.rect( - size=(width - 1, height - 1), - insert=(0.5, 0.5), - rx=4.5, - stroke=themes[theme]["border_color"], - fill=themes[theme]["bg_color"], - ) - ) - - d.add( - d.rect( - size=(width - 2 * padding, height - 2 * padding), - insert=(padding, padding), - fill="#eee" if debug else themes[theme]["bg_color"], - ) - ) - - dp = Group(transform=f"translate({padding}, {padding})") - - dp.add(d.text(header_text, insert=(0, 13), class_=f"{theme}-header")) - dp.add(d.text(subheader_text, insert=(0, 31), class_=f"{theme}-subheader")) - - return d, dp - - -def get_bar_section( - d: Drawing, - dataset: List[Tuple[str, str, List[Tuple[float, str]]]], - theme: str, - padding: int = 45, - bar_width: int = 210, -) -> Group: - section = Group(transform=f"translate(0, {padding})") - for i, (top_text, right_text, data_row) in enumerate(dataset): - translate = f"translate(0, {str(40 * i)})" - row = Group(transform=translate) - row.add(d.text(top_text, insert=(2, 15), class_=f"{theme}-lang-name")) - row.add( - d.text(right_text, insert=(bar_width + 10, 33), class_=f"{theme}-lang-name") - ) - - progress = Drawing(width=str(bar_width), x="0", y="25") - progress.add( - d.rect( - size=(bar_width, 8), - insert=(0, 0), - rx=5, - ry=5, - fill=themes[theme]["bar_color"], - ) - ) - total_percent, total_items = 0, len(data_row) - diff = max(0, 300 / bar_width - data_row[-1][0]) - for j, (percent, color) in enumerate(data_row): - color = color or DEFAULT_COLOR - if j == 0: - percent -= diff - elif j == total_items - 1: - percent += diff - bar_percent = bar_width * percent / 100 - bar_total = bar_width * total_percent / 100 - box_size, insert = (bar_percent, 8), (bar_total, 0) - progress.add(d.rect(size=box_size, insert=insert, rx=5, ry=5, fill=color)) - - width = min(bar_percent / 2, 5) - if total_items > 1: - box_left, box_right = j > 0, j < total_items - 1 - box_size, insert = bar_percent - 2 * width, bar_total + width - if box_left: - box_size += width - insert -= width - if box_right: - box_size += width - progress.add(d.rect(size=(box_size, 8), insert=(insert, 0), fill=color)) - - total_percent += percent - row.add(progress) - section.add(row) - return section - - -def get_lang_name_section( - d: Drawing, - data: List[Tuple[str, str]], - theme: str, - columns: int = 2, - padding: int = 80, -) -> Group: - section = Group(transform=f"translate(0, {padding})") - for i, x in enumerate(data): - x_translate = str((260 / columns) * (i % columns)) - y_translate = str(20 * (i // columns)) - lang = Group(transform=f"translate({x_translate}, {y_translate})") - lang.add(Circle(center=(5, 5), r=5, fill=(data[i][1] or DEFAULT_COLOR))) - lang.add(d.text(data[i][0], insert=(14, 9), class_=f"{theme}-lang-name")) - section.add(lang) - return section diff --git a/frontend/backend/src/render/top_langs.py b/frontend/backend/src/render/top_langs.py deleted file mode 100644 index cec169e8..00000000 --- a/frontend/backend/src/render/top_langs.py +++ /dev/null @@ -1,74 +0,0 @@ -# type: ignore - -from typing import List, Tuple - -from svgwrite import Drawing - -from src.models.svg import LanguageStats -from src.render.error import get_no_data_svg -from src.render.template import get_bar_section, get_lang_name_section, get_template -from src.utils import format_number - - -def get_top_langs_svg( - data: List[LanguageStats], - time_str: str, - use_percent: bool, - loc_metric: str, - complete: bool, - commits_excluded: int, - compact: bool, - use_animation: bool, - theme: str, -) -> Drawing: - header = "Most Used Languages" - subheader = time_str - if not use_percent: - subheader += " | " + ("LOC Changed" if loc_metric == "changed" else "LOC Added") - if not complete: - subheader += " | Incomplete (refresh to update)" - elif commits_excluded > 50: - subheader += f" | {commits_excluded} commits excluded" - - if len(data) <= 1: - return get_no_data_svg(header, subheader) - - d, dp = get_template( - width=300, - height=175 if compact else 285, - padding=20, - header_text=header, - subheader_text=subheader, - use_animation=use_animation, - debug=False, - theme=theme, - ) - - dataset: List[Tuple[str, str, List[Tuple[float, str]]]] = [] - padding, width = 0, 0 - if compact: - data_row = [(x.percent, x.color) for x in data[1:6]] - dataset.append(("", "", data_row)) - padding, width = 30, 260 - else: - max_length = max(data[i].loc for i in range(1, len(data))) - for x in data[1:6]: - if use_percent: - dataset.append((x.lang, f"{str(x.percent)}%", [(x.percent, x.color)])) - else: - percent = 100 * x.loc / max_length - dataset.append((x.lang, format_number(x.loc), [(percent, x.color)])) - padding, width = 45, 210 if use_percent else 195 - - dp.add( - get_bar_section( - d=d, dataset=dataset, theme=theme, padding=padding, bar_width=width - ) - ) - - langs = [(f"{x.lang} {str(x.percent)}%", x.color) for x in data[1:6]] - if compact: - dp.add(get_lang_name_section(d=d, data=langs, theme=theme)) - - d.add(dp) - return d diff --git a/frontend/backend/src/render/top_repos.py b/frontend/backend/src/render/top_repos.py deleted file mode 100644 index 08833364..00000000 --- a/frontend/backend/src/render/top_repos.py +++ /dev/null @@ -1,76 +0,0 @@ -# type: ignore - -from collections import defaultdict -from typing import List, Tuple - -from svgwrite import Drawing - -from src.models.svg import RepoStats -from src.render.error import get_no_data_svg -from src.render.template import get_bar_section, get_lang_name_section, get_template -from src.utils import format_number - - -def get_top_repos_svg( - data: List[RepoStats], - time_str: str, - loc_metric: str, - complete: bool, - commits_excluded: int, - use_animation: bool, - theme: str, -) -> Drawing: - header = "Most Contributed Repositories" - subheader = time_str - subheader += " | " + ("LOC Changed" if loc_metric == "changed" else "LOC Added") - if not complete: - subheader += " | Incomplete (refresh to update)" - elif commits_excluded > 50: - subheader += f" | {commits_excluded} commits excluded" - - if len(data) == 0: - return get_no_data_svg(header, subheader) - - d, dp = get_template( - width=300, - height=285, - padding=20, - header_text=header, - subheader_text=subheader, - use_animation=use_animation, - debug=False, - theme=theme, - ) - - dataset: List[Tuple[str, str, List[Tuple[float, str]]]] = [] - total = max(x.loc for x in data) - for x in data[:4]: - data_row = [ - (100 * lang.loc / total, lang.color) - for lang in sorted(x.langs, key=lambda x: x.loc, reverse=True) - ] - - name = "private/repository" if x.private else x.repo - dataset.append((name, format_number(x.loc), data_row)) - - dp.add( - get_bar_section(d=d, dataset=dataset, theme=theme, padding=45, bar_width=195) - ) - - langs = defaultdict(int) - for x in data[:4]: - for lang in x.langs: - langs[(lang.lang, lang.color)] += lang.loc - langs = sorted(langs.items(), key=lambda x: x[1], reverse=True) - langs = [lang[0] for lang in langs[:6]] - - columns = {1: 1, 2: 2, 3: 3, 4: 2, 5: 3, 6: 3}[len(langs)] - padding = 215 + (10 if columns == len(langs) else 0) - dp.add( - get_lang_name_section( - d=d, data=langs, theme=theme, columns=columns, padding=padding - ) - ) - - d.add(dp) - return d diff --git a/frontend/backend/src/routers/__init__.py b/frontend/backend/src/routers/__init__.py deleted file mode 100644 index 9003fcad..00000000 --- a/frontend/backend/src/routers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from src.routers.assets.assets import router as asset_router -from src.routers.auth.main import router as auth_router -from src.routers.dev import router as dev_router -from src.routers.users.main import router as user_router -from src.routers.wrapped import router as wrapped_router - -__all__ = ["asset_router", "auth_router", "dev_router", "user_router", "wrapped_router"] diff --git a/frontend/backend/src/routers/assets/__init__.py b/frontend/backend/src/routers/assets/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/routers/assets/assets.py b/frontend/backend/src/routers/assets/assets.py deleted file mode 100644 index 917dd45c..00000000 --- a/frontend/backend/src/routers/assets/assets.py +++ /dev/null @@ -1,14 +0,0 @@ -from fastapi import APIRouter, status -from fastapi.responses import FileResponse - -router = APIRouter() - - -@router.get("/error", status_code=status.HTTP_200_OK, include_in_schema=False) -async def get_error_img(): - return FileResponse("./src/routers/assets/assets/error.png") - - -@router.get("/stopwatch", status_code=status.HTTP_200_OK, include_in_schema=False) -async def get_stopwatch_img(): - return FileResponse("./src/routers/assets/assets/stopwatch.png") diff --git a/frontend/backend/src/routers/assets/assets/error.png b/frontend/backend/src/routers/assets/assets/error.png deleted file mode 100644 index 6282e1d4..00000000 Binary files a/frontend/backend/src/routers/assets/assets/error.png and /dev/null differ diff --git a/frontend/backend/src/routers/assets/assets/stopwatch.png b/frontend/backend/src/routers/assets/assets/stopwatch.png deleted file mode 100644 index 9467b20b..00000000 Binary files a/frontend/backend/src/routers/assets/assets/stopwatch.png and /dev/null differ diff --git a/frontend/backend/src/routers/auth/__init__.py b/frontend/backend/src/routers/auth/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/routers/auth/main.py b/frontend/backend/src/routers/auth/main.py deleted file mode 100644 index b3606853..00000000 --- a/frontend/backend/src/routers/auth/main.py +++ /dev/null @@ -1,8 +0,0 @@ -from fastapi import APIRouter - -from src.routers.auth.standalone import router as standalone_router -from src.routers.auth.website import router as website_router - -router = APIRouter() -router.include_router(standalone_router, prefix="") -router.include_router(website_router, prefix="/web") diff --git a/frontend/backend/src/routers/auth/standalone.py b/frontend/backend/src/routers/auth/standalone.py deleted file mode 100644 index 8104c8ff..00000000 --- a/frontend/backend/src/routers/auth/standalone.py +++ /dev/null @@ -1,46 +0,0 @@ -import logging -from typing import Optional - -from fastapi import APIRouter -from fastapi.responses import RedirectResponse - -from src.constants import OAUTH_CLIENT_ID -from src.processing.auth import authenticate, delete_user -from src.routers.decorators import get_redirect_url - -router = APIRouter() - - -@router.get("/signup/public") -def redirect_public(user_id: Optional[str] = None) -> RedirectResponse: - return RedirectResponse(get_redirect_url(private=False, user_id=user_id)) - - -@router.get("/signup/private") -def redirect_private(user_id: Optional[str] = None) -> RedirectResponse: - return RedirectResponse(get_redirect_url(private=True, user_id=user_id)) - - -@router.get("/redirect", include_in_schema=False) -async def redirect_return(code: str = "", private_access: bool = False) -> str: - try: - user_id = await authenticate(code=code, private_access=private_access) - return f"You ({user_id}) are now authenticated!" - except Exception as e: - logging.exception(e) - return "Unknown Error. Please try again later." - - -@router.get("/delete/{user_id}") -async def delete_account_auth(user_id: str) -> RedirectResponse: - return RedirectResponse( - get_redirect_url(prefix=f"delete/{user_id}", private=False, user_id=user_id) - ) - - -@router.get("/redirect/delete/{user_id}", include_in_schema=False) -async def delete_account(user_id: str) -> RedirectResponse: - await delete_user(user_id, user_key="", use_user_key=False) - return RedirectResponse( - f"https://github.com/settings/connections/applications/{OAUTH_CLIENT_ID}" - ) diff --git a/frontend/backend/src/routers/auth/website.py b/frontend/backend/src/routers/auth/website.py deleted file mode 100644 index 37b53211..00000000 --- a/frontend/backend/src/routers/auth/website.py +++ /dev/null @@ -1,53 +0,0 @@ -from typing import Any, Dict - -from fastapi import BackgroundTasks, status -from fastapi.responses import Response -from fastapi.routing import APIRouter - -from src.processing.auth import authenticate, delete_user, set_user_key -from src.routers.background import run_in_background -from src.utils import async_fail_gracefully - -router = APIRouter() - - -@router.post( - "/set_user_key/{code}/{user_key}", - status_code=status.HTTP_200_OK, - include_in_schema=False, - response_model=Dict[str, Any], -) -@async_fail_gracefully -async def set_user_key_endpoint(response: Response, code: str, user_key: str) -> str: - return await set_user_key(code, user_key) - - -@router.post( - "/login/{code}", - status_code=status.HTTP_200_OK, - include_in_schema=False, - response_model=Dict[str, Any], -) -@async_fail_gracefully -async def authenticate_endpoint( - response: Response, - background_tasks: BackgroundTasks, - code: str, - private_access: bool = False, -) -> str: - output, background_task = await authenticate(code, private_access) - if background_task is not None: - # set a background task to update the user - background_tasks.add_task(run_in_background, task=background_task) - return output - - -@router.get( - "/delete/{user_id}", - status_code=status.HTTP_200_OK, - include_in_schema=False, - response_model=Dict[str, Any], -) -@async_fail_gracefully -async def delete_user_endpoint(response: Response, user_id: str, user_key: str) -> bool: - return await delete_user(user_id, user_key=user_key) diff --git a/frontend/backend/src/routers/background.py b/frontend/backend/src/routers/background.py deleted file mode 100644 index 2892f02a..00000000 --- a/frontend/backend/src/routers/background.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Dict - -from src.aggregation.layer1 import query_user -from src.models.background import UpdateUserBackgroundTask - -# create a cache for the function -cache: Dict[str, Dict[str, bool]] = {"update_user": {}} - - -async def run_in_background(task: UpdateUserBackgroundTask): - if isinstance(task, UpdateUserBackgroundTask): # type: ignore - inputs = { - "user_id": task.user_id, - "access_token": task.access_token, - "private_access": task.private_access, - "start_date": task.start_date, - "end_date": task.end_date, - } - - inputs = {k: v for k, v in inputs.items() if v is not None} - - # check if the task is already running - if task.user_id in cache["update_user"]: - return - - # add the task to the cache - cache["update_user"][task.user_id] = True - - await query_user(**inputs) # type: ignore - - # remove the task from the cache - del cache["update_user"][task.user_id] diff --git a/frontend/backend/src/routers/decorators.py b/frontend/backend/src/routers/decorators.py deleted file mode 100644 index e6536644..00000000 --- a/frontend/backend/src/routers/decorators.py +++ /dev/null @@ -1,80 +0,0 @@ -import io -import logging -from datetime import datetime -from functools import wraps -from typing import Any, Callable, Dict, List, Optional - -from fastapi import Response, status -from starlette.responses import RedirectResponse -from svgwrite.drawing import Drawing # type: ignore - -from src.constants import OAUTH_CLIENT_ID, OAUTH_REDIRECT_URI -from src.render import get_error_svg - - -# for standalone auth routes -def get_redirect_url( - prefix: str = "", private: bool = False, user_id: Optional[str] = None -) -> str: - url = ( - "https://github.com/login/oauth/authorize?client_id=" - + OAUTH_CLIENT_ID - + "&redirect_uri=" - + OAUTH_REDIRECT_URI - + "/redirect" - ) - - # add prefix to redirect to different backend routes - if prefix != "": - url += f"/{prefix}" - - # add private flag to request correct permissions - if private: - url += "?private_access=True&scope=user,repo" - else: - url += "?private_access=False" - - # add user_id to hint if provided - if user_id is not None: - url += f"&login={user_id}" - - return url - - -# NOTE: implied async, sync not implemented yet -def svg_fail_gracefully(func: Callable[..., Any]): - @wraps(func) # needed to play nice with FastAPI decorator - async def wrapper( - response: Response, *args: List[Any], **kwargs: Dict[str, Any] - ) -> Any: - d: Drawing - start = datetime.now() - cache_max_age = 3600 - try: - d = await func(response, *args, **kwargs) - except LookupError as e: - if "user_id" in kwargs: - user_id: str = kwargs["user_id"] # type: ignore - url = get_redirect_url(private=False, user_id=user_id) - return RedirectResponse(url) - logging.exception(e) - d = get_error_svg() - cache_max_age = 0 - except Exception as e: - logging.exception(e) - d = get_error_svg() - cache_max_age = 0 - - sio = io.StringIO() - d.write(sio) # type: ignore - - print("SVG", datetime.now() - start) - - return Response( - sio.getvalue(), - media_type="image/svg+xml", - status_code=status.HTTP_200_OK, - headers={"Cache-Control": f"public, max-age={cache_max_age}"}, - ) - - return wrapper diff --git a/frontend/backend/src/routers/dev.py b/frontend/backend/src/routers/dev.py deleted file mode 100644 index 459d1f91..00000000 --- a/frontend/backend/src/routers/dev.py +++ /dev/null @@ -1,50 +0,0 @@ -from datetime import date, timedelta -from typing import Any, Dict, Optional - -from fastapi import APIRouter, Response, status - -from src.aggregation.layer0 import get_user_data -from src.data.mongo.secret import update_keys -from src.models import UserPackage, WrappedPackage -from src.processing.wrapped.package import get_wrapped_data -from src.utils import async_fail_gracefully, use_time_range - -router = APIRouter() - - -@router.get( - "/user/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any] -) -@async_fail_gracefully -async def get_user_raw( - response: Response, - user_id: str, - access_token: Optional[str] = None, - start_date: date = date.today() - timedelta(365), - end_date: date = date.today(), - time_range: str = "one_month", - timezone_str: str = "US/Eastern", - full: bool = False, -) -> UserPackage: - await update_keys() - start_date, end_date, _ = use_time_range(time_range, start_date, end_date) - return await get_user_data( - user_id, start_date, end_date, timezone_str, access_token - ) - - -@router.get( - "/wrapped/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any] -) -@async_fail_gracefully -async def get_wrapped_user_raw( - response: Response, - user_id: str, - year: int = 2024, - access_token: Optional[str] = None, -) -> WrappedPackage: - await update_keys() - user_data = await get_user_data( - user_id, date(year, 1, 1), date(year, 12, 31), "US/Eastern", access_token - ) - return get_wrapped_data(user_data, year) diff --git a/frontend/backend/src/routers/users/__init__.py b/frontend/backend/src/routers/users/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/src/routers/users/db.py b/frontend/backend/src/routers/users/db.py deleted file mode 100644 index d5e07b5e..00000000 --- a/frontend/backend/src/routers/users/db.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import Any, Dict, Optional - -from fastapi import APIRouter, Response, status - -from src.data.mongo.secret import update_keys -from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user -from src.utils import async_fail_gracefully - -router = APIRouter() - - -@router.get( - "/update_keys", - status_code=status.HTTP_200_OK, - include_in_schema=False, - response_model=Dict[str, Any], -) -@async_fail_gracefully -async def update_keys_endpoint(response: Response) -> bool: - await update_keys(no_cache=True) - return True - - -@router.get( - "/get/metadata/{user_id}", - status_code=status.HTTP_200_OK, - include_in_schema=False, - response_model=Dict[str, Any], -) -@async_fail_gracefully -async def get_db_public_user( - response: Response, user_id: str, no_cache: bool = False -) -> Optional[PublicUserModel]: - return await db_get_public_user(user_id, no_cache=no_cache) - - -""" -@router.get("/get/{user_id}", status_code=status.HTTP_200_OK, include_in_schema=False) -@async_fail_gracefully -async def get_db_user( - response: Response, user_id: str, no_cache: bool = False -) -> Optional[ExternalUserModel]: - user: Optional[UserModel] = await get_user_by_user_id(user_id, no_cache=no_cache) - if user is None: - return None - return ExternalUserModel.parse_obj(user.dict()) -""" diff --git a/frontend/backend/src/routers/users/main.py b/frontend/backend/src/routers/users/main.py deleted file mode 100644 index bf7f5fe2..00000000 --- a/frontend/backend/src/routers/users/main.py +++ /dev/null @@ -1,41 +0,0 @@ -from datetime import date, timedelta -from typing import Any, Dict, Optional - -from fastapi import APIRouter, BackgroundTasks, Response, status - -from src.aggregation.layer2 import get_user -from src.models import UserPackage -from src.routers.background import run_in_background -from src.routers.users.db import router as db_router -from src.routers.users.svg import router as svg_router -from src.utils import async_fail_gracefully - -router = APIRouter() -router.include_router(db_router, prefix="/db") -router.include_router(svg_router, prefix="/svg") - - -""" -ANALYTICS -""" - - -@router.get("/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) -@async_fail_gracefully -async def get_user_endpoint( - response: Response, - background_tasks: BackgroundTasks, - user_id: str, - start_date: date = date.today() - timedelta(365), - end_date: date = date.today(), - timezone_str: str = "US/Eastern", - no_cache: bool = False, -) -> Optional[UserPackage]: - output, _, background_task = await get_user( - user_id, start_date, end_date, no_cache=no_cache - ) - if background_task is not None: - # set a background task to update the user - background_tasks.add_task(run_in_background, task=background_task) - - return output diff --git a/frontend/backend/src/routers/users/svg.py b/frontend/backend/src/routers/users/svg.py deleted file mode 100644 index 2ad36875..00000000 --- a/frontend/backend/src/routers/users/svg.py +++ /dev/null @@ -1,127 +0,0 @@ -from datetime import date, timedelta -from typing import Any - -from fastapi import BackgroundTasks, Response, status -from fastapi.responses import HTMLResponse -from fastapi.routing import APIRouter - -from src.processing.user import get_top_languages, get_top_repos, svg_base -from src.render import ( - get_empty_demo_svg, - get_loading_svg, - get_top_langs_svg, - get_top_repos_svg, -) -from src.routers.background import run_in_background -from src.routers.decorators import svg_fail_gracefully - -router = APIRouter() - - -@router.get( - "/{user_id}/langs", status_code=status.HTTP_200_OK, response_class=HTMLResponse -) -@svg_fail_gracefully -async def get_user_lang_svg( - response: Response, - background_tasks: BackgroundTasks, - user_id: str, - start_date: date = date.today() - timedelta(30), - end_date: date = date.today(), - time_range: str = "one_year", - timezone_str: str = "US/Eastern", - use_percent: bool = False, - include_private: bool = False, - loc_metric: str = "added", - compact: bool = False, - demo: bool = False, - no_cache: bool = False, - use_animation: bool = True, - theme: str = "classic", -) -> Any: - output, complete, background_task, time_str = await svg_base( - user_id, start_date, end_date, time_range, demo, no_cache - ) - if background_task is not None: - # set a background task to update the user - background_tasks.add_task(run_in_background, task=background_task) - - # if no data, return loading svg - if output is None: - return get_loading_svg() - - # get top languages - processed, commits_excluded = get_top_languages(output, loc_metric, include_private) - return get_top_langs_svg( - data=processed, - time_str=time_str, - use_percent=use_percent, - loc_metric=loc_metric, - complete=complete, - commits_excluded=commits_excluded, - compact=compact, - use_animation=use_animation, - theme=theme, - ) - - -@router.get( - "/{user_id}/repos", status_code=status.HTTP_200_OK, response_class=HTMLResponse -) -@svg_fail_gracefully -async def get_user_repo_svg( - response: Response, - background_tasks: BackgroundTasks, - user_id: str, - start_date: date = date.today() - timedelta(30), - end_date: date = date.today(), - time_range: str = "one_year", - timezone_str: str = "US/Eastern", - include_private: bool = False, - group: str = "none", - loc_metric: str = "added", - demo: bool = False, - no_cache: bool = False, - use_animation: bool = True, - theme: str = "classic", -) -> Any: - output, complete, background_task, time_str = await svg_base( - user_id, start_date, end_date, time_range, demo, no_cache - ) - if background_task is not None: - # set a background task to update the user - background_tasks.add_task(run_in_background, task=background_task) - - # if no data, return loading svg - if output is None: - return get_loading_svg() - - # get top repos - processed, commits_excluded = get_top_repos( - output, loc_metric, include_private, group - ) - return get_top_repos_svg( - data=processed, - time_str=time_str, - loc_metric=loc_metric, - complete=complete, - commits_excluded=commits_excluded, - use_animation=use_animation, - theme=theme, - ) - - -@router.get( - "/demo", - status_code=status.HTTP_200_OK, - response_class=HTMLResponse, - include_in_schema=False, -) -@svg_fail_gracefully -async def get_demo_svg(response: Response, card: str) -> Any: - if card == "langs": - return get_empty_demo_svg("Most Used Languages") - elif card == "repos": - return get_empty_demo_svg("Most Contributed Repositories") - else: - return get_empty_demo_svg(card) diff --git a/frontend/backend/src/routers/wrapped.py b/frontend/backend/src/routers/wrapped.py deleted file mode 100644 index ebb8a11f..00000000 --- a/frontend/backend/src/routers/wrapped.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Any, Dict, Optional - -from fastapi import APIRouter, Response, status - -from src.aggregation.layer2 import get_is_valid_user -from src.models import WrappedPackage -from src.processing.wrapped import query_wrapped_user -from src.utils import async_fail_gracefully - -router = APIRouter() - - -@router.get( - "/valid/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any] -) -@async_fail_gracefully -async def check_valid_user(response: Response, user_id: str) -> str: - return await get_is_valid_user(user_id) - - -@router.get("/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) -@async_fail_gracefully -async def get_wrapped_user( - response: Response, user_id: str, year: int = 2024, no_cache: bool = False -) -> Optional[WrappedPackage]: - valid_user = await get_is_valid_user(user_id) - if "Valid user" not in valid_user: - return WrappedPackage.empty() - - return await query_wrapped_user(user_id, year, no_cache=no_cache) diff --git a/frontend/backend/src/utils/__init__.py b/frontend/backend/src/utils/__init__.py deleted file mode 100644 index 52532cbd..00000000 --- a/frontend/backend/src/utils/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from src.utils.alru_cache import alru_cache -from src.utils.decorators import async_fail_gracefully, fail_gracefully -from src.utils.gather import gather -from src.utils.utils import date_to_datetime, format_number, use_time_range - -__all__ = [ - "alru_cache", - "async_fail_gracefully", - "fail_gracefully", - "gather", - "date_to_datetime", - "format_number", - "use_time_range", -] diff --git a/frontend/backend/src/utils/alru_cache.py b/frontend/backend/src/utils/alru_cache.py deleted file mode 100644 index c6b0305d..00000000 --- a/frontend/backend/src/utils/alru_cache.py +++ /dev/null @@ -1,79 +0,0 @@ -from datetime import datetime, timedelta -from functools import wraps -from typing import ( - Any, - Awaitable, - Callable, - Dict, - FrozenSet, - List, - ParamSpec, - Tuple, - TypeVar, -) - -Param = ParamSpec("Param") -TOutput = TypeVar("TOutput") - -TKey = Tuple[Tuple[Any, ...], FrozenSet[Tuple[str, Any]]] - - -def alru_cache(max_size: int = 128, ttl: timedelta = timedelta(minutes=1)): - def decorator( - func: Callable[Param, Awaitable[Tuple[bool, TOutput]]] - ) -> Callable[Param, Awaitable[TOutput]]: - cache: Dict[TKey, Tuple[datetime, TOutput]] = {} - keys: List[TKey] = [] - - def in_cache(key: TKey) -> bool: - # key not in cache - if key not in cache: - return False - - # key in cache but expired - if datetime.now() - cache[key][0] > ttl: - return False - - # key in cache and not expired - return True - - def update_cache_and_return(key: TKey, flag: bool, value: TOutput) -> TOutput: - # if flag = False, do not update cache and return value - if not flag: - return value - - # if flag = True, update cache - now = datetime.now() - cache[key] = (now, value) - keys.append(key) - - # remove oldest key if cache is full - if len(keys) > max_size: - try: - # Should not raise KeyError, but just in case - del cache[keys.pop(0)] - except KeyError: - # Already deleted by another thread - pass - - # return value from cache - return value # equal to cache[key][1] - - @wraps(func) - async def wrapper(*args: Param.args, **kwargs: Param.kwargs) -> TOutput: - key: TKey = tuple(args), frozenset( - [(k, v) for k, v in kwargs.items() if k not in ["no_cache"]] - ) - if "no_cache" in kwargs and kwargs["no_cache"]: - (flag, value) = await func(*args, **kwargs) - return update_cache_and_return(key, flag, value) - - if in_cache(key): - return cache[key][1] - - (flag, value) = await func(*args, **kwargs) - return update_cache_and_return(key, flag, value) - - return wrapper - - return decorator diff --git a/frontend/backend/src/utils/decorators.py b/frontend/backend/src/utils/decorators.py deleted file mode 100644 index dd814ed2..00000000 --- a/frontend/backend/src/utils/decorators.py +++ /dev/null @@ -1,46 +0,0 @@ -import logging -from datetime import datetime -from functools import wraps -from typing import Any, Callable, Dict, List - -from fastapi import Response, status - - -def fail_gracefully(func: Callable[..., Any]): - @wraps(func) # needed to play nice with FastAPI decorator - def wrapper(response: Response, *args: List[Any], **kwargs: Dict[str, Any]) -> Any: - start = datetime.now() - try: - data = func(response, *args, **kwargs) - return {"data": data, "message": "200 OK", "time": datetime.now() - start} - except Exception as e: - logging.exception(e) - response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - return { - "data": [], - "message": f"Error {str(e)}", - "time": datetime.now() - start, - } - - return wrapper - - -def async_fail_gracefully(func: Callable[..., Any]): - @wraps(func) # needed to play nice with FastAPI decorator - async def wrapper( - response: Response, *args: List[Any], **kwargs: Dict[str, Any] - ) -> Any: - start = datetime.now() - try: - data = await func(response, *args, **kwargs) - return {"data": data, "message": "200 OK", "time": datetime.now() - start} - except Exception as e: - logging.exception(e) - response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - return { - "data": [], - "message": f"Error {str(e)}", - "time": datetime.now() - start, - } - - return wrapper diff --git a/frontend/backend/src/utils/gather.py b/frontend/backend/src/utils/gather.py deleted file mode 100644 index ac43cdec..00000000 --- a/frontend/backend/src/utils/gather.py +++ /dev/null @@ -1,42 +0,0 @@ -import asyncio -from functools import partial, wraps -from typing import Any, Callable, Dict, List - - -def async_function(func: Callable[..., Any]) -> Callable[..., Any]: - @wraps(func) - async def run(*args: List[Any], **kwargs: Dict[str, Any]) -> Any: - loop = asyncio.get_event_loop() - pfunc = partial(func, *args, **kwargs) - return await loop.run_in_executor(executor=None, func=pfunc) - - return run - - -async def gather_with_concurrency( - n: int, *tasks: List[Callable[..., Any]] -) -> List[Any]: - semaphore = asyncio.Semaphore(n) - - async def sem_task(task: Callable[..., Any]) -> Any: - async with semaphore: - return await task # type: ignore - - return await asyncio.gather(*(sem_task(task) for task in tasks)) # type: ignore - - -async def gather( - funcs: List[Callable[..., Any]], - args_dicts: List[Dict[str, Any]], - max_threads: int = 5, -) -> List[Any]: - """runs the given functions asynchronously""" - - output: List[Any] = list( - await gather_with_concurrency( - max_threads, - *(async_function(func)(**kwargs) for func, kwargs in zip(funcs, args_dicts)) - ) - ) - - return output diff --git a/frontend/backend/src/utils/utils.py b/frontend/backend/src/utils/utils.py deleted file mode 100644 index 969bb9d8..00000000 --- a/frontend/backend/src/utils/utils.py +++ /dev/null @@ -1,45 +0,0 @@ -from datetime import date, datetime, timedelta -from typing import Tuple - - -def date_to_datetime( - dt: date, hour: int = 0, minute: int = 0, second: int = 0 -) -> datetime: - return datetime(dt.year, dt.month, dt.day, hour, minute, second) - - -# returns start date, end date, string representing time range -def use_time_range( - time_range: str, start_date: date, end_date: date -) -> Tuple[date, date, str]: - duration_options = { - "one_month": (30, "Past 1 Month"), - "three_months": (90, "Past 3 Months"), - "six_months": (180, "Past 6 Months"), - "one_year": (365, "Past 1 Year"), - "all_time": (365 * 10, "All Time"), - } - - start_str = start_date.strftime("X%m/X%d/%Y").replace("X0", "X").replace("X", "") - end_str = end_date.strftime("X%m/X%d/%Y").replace("X0", "X").replace("X", "") - if end_date == date.today(): - end_str = "Present" - time_str = f"{start_str} - {end_str}" - - if time_range in duration_options: - days, time_str = duration_options[time_range] - end_date = date.today() - start_date = date.today() - timedelta(days) - - return start_date, end_date, time_str - - -def format_number(num: int) -> str: - if num > 10000: - return f"~{str(num // 1000)}k lines" - elif num > 1000: - return f"~{str(num // 100 / 10)}k lines" - elif num > 100: - return f"~{str(num // 100 * 100)} lines" - else: - return "<100 lines" diff --git a/frontend/backend/tests/__init__.py b/frontend/backend/tests/__init__.py deleted file mode 100644 index 134bb6a6..00000000 --- a/frontend/backend/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from dotenv import find_dotenv, load_dotenv - -load_dotenv(find_dotenv(), verbose=True) diff --git a/frontend/backend/tests/aggregation/__init__.py b/frontend/backend/tests/aggregation/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/aggregation/layer0/__init__.py b/frontend/backend/tests/aggregation/layer0/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/aggregation/layer0/test_contributions.py b/frontend/backend/tests/aggregation/layer0/test_contributions.py deleted file mode 100644 index 47afd4d8..00000000 --- a/frontend/backend/tests/aggregation/layer0/test_contributions.py +++ /dev/null @@ -1,20 +0,0 @@ -from datetime import date, timedelta - -from aiounittest.case import AsyncTestCase - -from src.aggregation.layer0.contributions import get_contributions -from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID -from src.models import UserContributions - - -class TestTemplate(AsyncTestCase): - async def test_get_contributions(self): - response = await get_contributions( - user_id=USER_ID, - start_date=date.today() - timedelta(days=30), - end_date=date.today(), - access_token=TOKEN, - ) - self.assertIsInstance(response, UserContributions) - - # TODO: Add further validation diff --git a/frontend/backend/tests/aggregation/layer0/test_follows.py b/frontend/backend/tests/aggregation/layer0/test_follows.py deleted file mode 100644 index 7664c806..00000000 --- a/frontend/backend/tests/aggregation/layer0/test_follows.py +++ /dev/null @@ -1,13 +0,0 @@ -import unittest - -from src.aggregation.layer0.follows import get_user_follows -from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID -from src.models import UserFollows - - -class TestTemplate(unittest.TestCase): - def test_get_follows(self): - response = get_user_follows(USER_ID, TOKEN) - self.assertIsInstance(response, UserFollows) - - # TODO: Add further validation diff --git a/frontend/backend/tests/data/__init__.py b/frontend/backend/tests/data/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/data/github/__init__.py b/frontend/backend/tests/data/github/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/data/github/auth/__init__.py b/frontend/backend/tests/data/github/auth/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/data/github/auth/test_main.py b/frontend/backend/tests/data/github/auth/test_main.py deleted file mode 100644 index decda2e4..00000000 --- a/frontend/backend/tests/data/github/auth/test_main.py +++ /dev/null @@ -1,16 +0,0 @@ -import unittest - -from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID -from src.data.github.auth.main import get_unknown_user - - -class TestTemplate(unittest.TestCase): - def test_get_unknown_user_valid(self): - user_id = get_unknown_user(TOKEN) - self.assertEqual(user_id, USER_ID) - - def test_get_unknown_user_invalid(self): - user_id = get_unknown_user("") - self.assertEqual(user_id, None) - - # TODO: test authenticate() diff --git a/frontend/backend/tests/data/github/graphql/__init__.py b/frontend/backend/tests/data/github/graphql/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/data/github/graphql/test_commits.py b/frontend/backend/tests/data/github/graphql/test_commits.py deleted file mode 100644 index a7190597..00000000 --- a/frontend/backend/tests/data/github/graphql/test_commits.py +++ /dev/null @@ -1,34 +0,0 @@ -import unittest - -from src.constants import TEST_NODE_IDS as NODE_IDS, TEST_TOKEN as TOKEN -from src.data.github.graphql import RawCommit, get_commits - - -class TestTemplate(unittest.TestCase): - def test_get_commits(self): - node_ids = get_commits(access_token=TOKEN, node_ids=NODE_IDS, catch_errors=True) - - # assert returns equal number of commits - self.assertIsInstance(node_ids, list) - self.assertEqual(len(node_ids), len(NODE_IDS)) - - self.assertIsInstance(node_ids[0], RawCommit) - - def test_get_commits_invalid_access_token(self): - node_ids = get_commits(access_token="", node_ids=NODE_IDS, catch_errors=True) - - # assert returns list of Nones - self.assertIsInstance(node_ids, list) - self.assertEqual(len(node_ids), len(NODE_IDS)) - self.assertIsInstance(node_ids[0], type(None)) - - def test_get_commits_invalid_node_ids(self): - node_ids = get_commits( - access_token=TOKEN, - node_ids=[NODE_IDS[0], "", NODE_IDS[1]], - catch_errors=True, - ) - - self.assertIsInstance(node_ids[0], RawCommit) - self.assertIsInstance(node_ids[1], type(None)) - self.assertIsInstance(node_ids[2], RawCommit) diff --git a/frontend/backend/tests/data/github/graphql/test_repo.py b/frontend/backend/tests/data/github/graphql/test_repo.py deleted file mode 100644 index 8e81ee5c..00000000 --- a/frontend/backend/tests/data/github/graphql/test_repo.py +++ /dev/null @@ -1,34 +0,0 @@ -import unittest - -from src.constants import ( - TEST_REPO as REPO, - TEST_TOKEN as TOKEN, - TEST_USER_ID as USER_ID, -) -from src.data.github.graphql import RawRepo, get_repo - - -class TestTemplate(unittest.TestCase): - def test_get_repo(self): - repo: RawRepo = get_repo( # type: ignore - access_token=TOKEN, owner=USER_ID, repo=REPO, catch_errors=True - ) - - # assert returns equal number of commits - self.assertIsInstance(repo, RawRepo) - self.assertEqual(repo.is_private, False) - self.assertGreater(repo.fork_count, 0) - self.assertGreater(repo.stargazer_count, 0) - - def test_get_repo_invalid_access_token(self): - repo = get_repo(access_token="", owner=USER_ID, repo=REPO, catch_errors=True) - - # assert returns None - self.assertIsInstance(repo, type(None)) - - def test_get_commits_invalid_args(self): - repo = get_repo( - access_token=TOKEN, owner="abc123", repo=REPO, catch_errors=True - ) - - self.assertIsInstance(repo, type(None)) diff --git a/frontend/backend/tests/data/github/graphql/test_user_contribs.py b/frontend/backend/tests/data/github/graphql/test_user_contribs.py deleted file mode 100644 index f8e7b2be..00000000 --- a/frontend/backend/tests/data/github/graphql/test_user_contribs.py +++ /dev/null @@ -1,32 +0,0 @@ -import unittest -from datetime import datetime, timedelta - -from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID -from src.data.github.graphql import ( - RawCalendar, - RawEvents, - get_user_contribution_calendar, - get_user_contribution_events, -) - - -class TestTemplate(unittest.TestCase): - def test_get_user_contribution_calendar(self): - response = get_user_contribution_calendar( - user_id=USER_ID, - access_token=TOKEN, - start_date=datetime.now() - timedelta(days=30), - end_date=datetime.now(), - ) - - self.assertIsInstance(response, RawCalendar) - - def test_get_user_contribution_events(self): - response = get_user_contribution_events( - user_id=USER_ID, - access_token=TOKEN, - start_date=datetime.now() - timedelta(days=30), - end_date=datetime.now(), - ) - - self.assertIsInstance(response, RawEvents) diff --git a/frontend/backend/tests/data/github/graphql/test_user_follows.py b/frontend/backend/tests/data/github/graphql/test_user_follows.py deleted file mode 100644 index 65be59bd..00000000 --- a/frontend/backend/tests/data/github/graphql/test_user_follows.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID -from src.data.github.graphql import RawFollows, get_user_followers, get_user_following - - -class TestTemplate(unittest.TestCase): - def test_get_user_followers(self): - response = get_user_followers(user_id=USER_ID, access_token=TOKEN) - self.assertIsInstance(response, RawFollows) - - response = get_user_followers(user_id=USER_ID, access_token=TOKEN, first=1) - self.assertLessEqual(len(response.nodes), 1) - - def test_get_user_following(self): - response = get_user_following(user_id=USER_ID, access_token=TOKEN) - self.assertIsInstance(response, RawFollows) - - response = get_user_following(user_id=USER_ID, access_token=TOKEN, first=1) - self.assertLessEqual(len(response.nodes), 1) diff --git a/frontend/backend/tests/data/github/rest/__init__.py b/frontend/backend/tests/data/github/rest/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/data/github/rest/test_commit.py b/frontend/backend/tests/data/github/rest/test_commit.py deleted file mode 100644 index d37c27ae..00000000 --- a/frontend/backend/tests/data/github/rest/test_commit.py +++ /dev/null @@ -1,26 +0,0 @@ -import unittest - -from src.constants import ( - TEST_REPO as REPO, - TEST_SHA as SHA, - TEST_TOKEN as TOKEN, - TEST_USER_ID as USER_ID, -) -from src.data.github.rest import RawCommitFile, get_commit_files - - -class TestTemplate(unittest.TestCase): - def test_get_commit_files(self): - commits = get_commit_files( - access_token=TOKEN, owner=USER_ID, repo=REPO, sha=SHA - ) - self.assertIsInstance(commits, list) - self.assertIsInstance(commits[0], RawCommitFile) # type: ignore - - def test_get_commit_files_invalid_access_token(self): - repo = get_commit_files(access_token="", owner=USER_ID, repo=REPO, sha=SHA) - self.assertEqual(repo, None) - - def test_get_commit_files_invalid_args(self): - repo = get_commit_files(access_token=TOKEN, owner="abc123", repo=REPO, sha=SHA) - self.assertEqual(repo, None) diff --git a/frontend/backend/tests/data/github/rest/test_repo.py b/frontend/backend/tests/data/github/rest/test_repo.py deleted file mode 100644 index 06e753f2..00000000 --- a/frontend/backend/tests/data/github/rest/test_repo.py +++ /dev/null @@ -1,23 +0,0 @@ -import unittest - -from src.constants import ( - TEST_REPO as REPO, - TEST_TOKEN as TOKEN, - TEST_USER_ID as USER_ID, -) -from src.data.github.rest import RawCommit, get_repo_commits - - -class TestTemplate(unittest.TestCase): - def test_get_repo_commits(self): - commits = get_repo_commits(access_token=TOKEN, owner=USER_ID, repo=REPO) - self.assertIsInstance(commits, list) - self.assertIsInstance(commits[0], RawCommit) - - def test_get_repo_commits_invalid_access_token(self): - repo = get_repo_commits(access_token="", owner=USER_ID, repo=REPO) - self.assertEqual(repo, []) - - def test_get_repo_commits_invalid_args(self): - repo = get_repo_commits(access_token=TOKEN, owner="abc123", repo=REPO) - self.assertEqual(repo, []) diff --git a/frontend/backend/tests/utils/__init__.py b/frontend/backend/tests/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/backend/tests/utils/test_alru_cache.py b/frontend/backend/tests/utils/test_alru_cache.py deleted file mode 100644 index b9dd4235..00000000 --- a/frontend/backend/tests/utils/test_alru_cache.py +++ /dev/null @@ -1,111 +0,0 @@ -from asyncio import sleep -from datetime import timedelta -from typing import Tuple - -from aiounittest.case import AsyncTestCase - -from src.utils import alru_cache - - -class TestTemplate(AsyncTestCase): - async def test_basic_alru_cache(self): - count = 0 - - @alru_cache() - async def f(x: int) -> Tuple[bool, int]: - nonlocal count - count += 1 - return (True, x) - - assert count == 0 - assert await f(1) == 1 - assert count == 1 - assert await f(1) == 1 - assert count == 1 - assert await f(2) == 2 - assert count == 2 - assert await f(2) == 2 - assert count == 2 - assert await f(1) == 1 - assert count == 2 - - async def test_alru_cache_with_flag(self): - count = 0 - - @alru_cache() - async def f(x: int) -> Tuple[bool, int]: - nonlocal count - count += 1 - return (count % 2 == 0, x) - - assert count == 0 - assert await f(1) == 1 - assert count == 1 - assert await f(1) == 1 - assert count == 2 - assert await f(2) == 2 - assert count == 3 - assert await f(3) == 3 - assert count == 4 - assert await f(3) == 3 - assert count == 4 - - async def test_alru_cache_with_maxsize(self): - count = 0 - - @alru_cache(max_size=2) - async def f(x: int) -> Tuple[bool, int]: - nonlocal count - count += 1 - return (True, x) - - assert count == 0 - assert await f(1) == 1 - assert count == 1 - assert await f(2) == 2 - assert count == 2 - assert await f(3) == 3 - assert count == 3 - assert await f(1) == 1 - assert count == 4 - - async def test_alru_cache_with_ttl(self): - count = 0 - - @alru_cache(ttl=timedelta(milliseconds=1)) - async def f(x: int) -> Tuple[bool, int]: - nonlocal count - count += 1 - return (True, x) - - assert count == 0 - assert await f(1) == 1 - assert count == 1 - assert await f(1) == 1 - assert count == 1 - await sleep(0.01) - assert await f(1) == 1 - assert count == 2 - - async def test_alru_cache_with_no_cache(self): - count = 0 - - @alru_cache() - async def f(x: int, no_cache: bool = False) -> Tuple[bool, int]: - nonlocal count - count += 1 - return (True, x) - - assert count == 0 - assert await f(1) == 1 - assert count == 1 - assert await f(1) == 1 - assert count == 1 - assert await f(2) == 2 - assert count == 2 - assert await f(2, no_cache=True) == 2 - assert count == 3 - assert await f(3) == 3 - assert count == 4 - assert await f(3, no_cache=False) == 3 - assert count == 4 diff --git a/frontend/backend/transfer_mongodb.bash b/frontend/backend/transfer_mongodb.bash deleted file mode 100644 index 4a1d0e8d..00000000 --- a/frontend/backend/transfer_mongodb.bash +++ /dev/null @@ -1,31 +0,0 @@ -if [ $# -eq 0 ]; then - echo "Usage: $0 " - exit 1 -fi - -# Export -mongoexport --uri "mongodb+srv://backend.aqlpb.mongodb.net/" --db dev_backend --collection secrets --username root --password "$1" > ./dev_secrets.json -mongoexport --uri "mongodb+srv://backend.aqlpb.mongodb.net/" --db dev_backend --collection users --username root --password "$1" > ./dev_users.json -mongoexport --uri "mongodb+srv://backend.aqlpb.mongodb.net/" --db dev_backend --collection user_months --username root --password "$1" > ./dev_user_months.json - -mongoexport --uri "mongodb+srv://backend.aqlpb.mongodb.net/" --db prod_backend --collection secrets --username root --password "$1" > ./prod_secrets.json -mongoexport --uri "mongodb+srv://backend.aqlpb.mongodb.net/" --db prod_backend --collection users --username root --password "$1" > ./prod_users.json -mongoexport --uri "mongodb+srv://backend.aqlpb.mongodb.net/" --db prod_backend --collection user_months --username root --password "$1" > ./prod_user_months.json - -# Import -mongoimport --uri "mongodb+srv://backend2.e50j8dp.mongodb.net/" --db dev_backend --collection secrets --username root --password "$1" < ./dev_secrets.json -mongoimport --uri "mongodb+srv://backend2.e50j8dp.mongodb.net/" --db dev_backend --collection users --username root --password "$1" < ./dev_users.json -mongoimport --uri "mongodb+srv://backend2.e50j8dp.mongodb.net/" --db dev_backend --collection user_months --username root --password "$1" < ./dev_user_months.json - -mongoimport --uri "mongodb+srv://backend2.e50j8dp.mongodb.net/" --db prod_backend --collection secrets --username root --password "$1" < ./prod_secrets.json -mongoimport --uri "mongodb+srv://backend2.e50j8dp.mongodb.net/" --db prod_backend --collection users --username root --password "$1" < ./prod_users.json -mongoimport --uri "mongodb+srv://backend2.e50j8dp.mongodb.net/" --db prod_backend --collection user_months --username root --password "$1" < ./prod_user_months.json - -# Remove -rm ./dev_secrets.json -rm ./dev_users.json -rm ./dev_user_months.json - -rm ./prod_secrets.json -rm ./prod_users.json -rm ./prod_user_months.json \ No newline at end of file diff --git a/frontend/backend/yarn.lock b/frontend/backend/yarn.lock deleted file mode 100644 index fb57ccd1..00000000 --- a/frontend/backend/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - diff --git a/frontend/frontend/src/pages/Home/Home.js b/frontend/frontend/src/pages/Home/Home.js index 615a64e3..59ae3a89 100644 --- a/frontend/frontend/src/pages/Home/Home.js +++ b/frontend/frontend/src/pages/Home/Home.js @@ -17,8 +17,6 @@ import { HOST } from '../../constants'; import { CardTypes } from '../../utils'; const HomeScreen = () => { - console.log('new env var:', process.env.VERCEL_RELATED_PROJECTS); - const [isLoading, setIsLoading] = useState(false); const userId = useSelector((state) => state.user.userId); diff --git a/frontend/vercel.json b/frontend/vercel.json index 1e10f21e..d7a899e6 100644 --- a/frontend/vercel.json +++ b/frontend/vercel.json @@ -1,6 +1,5 @@ { "buildCommand": "yarn build-trends", "outputDirectory": "build", - "installCommand": "yarn install", - "relatedProjects": ["prj_ubTjOvYDA9zS24cT9hMARICRm9Vm"] + "installCommand": "yarn install" } \ No newline at end of file