diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000000000000000000000000000000000..b6d1d796b9cba39356eb282a50bbdbf3fc6fe1a3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,49 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "Existing Docker Compose (Extend)", + + // Update the 'dockerComposeFile' list if you have more compose files or use different names. + // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. + "dockerComposeFile": [ + "../docker-compose.yml", + "docker-compose.yml" + ], + + // The 'service' property is the name of the service for the container that VS Code should + // use. Update this value and .devcontainer/docker-compose.yml to the real service name. + "service": "streamlit", + + // The optional 'workspaceFolder' property is the path VS Code should open by default when + // connected. This is typically a file mount in .devcontainer/docker-compose.yml + "workspaceFolder": "/app", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-toolsai.jupyter" + ] + } + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": [], + + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + // "shutdownAction": "none", + + // Uncomment the next line to run commands after the container is created. + // "postCreateCommand": "cat /etc/os-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "devcontainer" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..8bfff8fc16cc08f596e18afe663ebdd28ae9a864 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,27 @@ +version: '3.8' +services: + # Update this to the name of the service you want to work with in your docker-compose.yml file + streamlit: + # Uncomment if you want to override the service's Dockerfile to one in the .devcontainer + # folder. Note that the path of the Dockerfile and context is relative to the *primary* + # docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile" + # array). The sample below assumes your primary file is in the root of your project. + # + build: + dockerfile: ./Dockerfile + context: ./ + target: development + + volumes: + # Update this to wherever you want VS Code to mount the folder of your project + - .:/app:cached + + # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. + # cap_add: + # - SYS_PTRACE + # security_opt: + # - seccomp:unconfined + + # Overrides default command so things don't shut down after the process ends. + command: ["/bin/sh -c \"while sleep 1000; do :; done\""] + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d3b1030ff582ae1c3554e60c137ec328202f592d --- /dev/null +++ b/.gitignore @@ -0,0 +1,166 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Streamlit +secrets.toml + +.ipynb_checkpoints +*.ipynb diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..55f9833195ab50bedfe862edada9246b98f5b984 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +FROM python:3.11-slim as base + +ENV APP_BASE_PATH="/app" +ENV APP_SRC_PATH=${APP_BASE_PATH}/src +ENV PYTHONPATH="${APP_SRC_PATH}:${PYTHONPATH}" +WORKDIR $APP_BASE_PATH + +RUN apt-get update && apt-get install -y \ + build-essential \ + curl \ + software-properties-common \ + git \ + && rm -rf /var/lib/apt/lists/* + +RUN pip3 install pip-tools +COPY ./pyproject.toml . + +FROM base as pip-service + +COPY ./requirements.txt . +RUN pip3 install -r requirements.txt + +FROM pip-service as service-setup +EXPOSE 8501 +HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health +ENTRYPOINT ["./start.sh"] + +FROM pip-service as service +WORKDIR $APP_SRC_PATH +COPY ./src . + +FROM service-setup as development +WORKDIR $APP_BASE_PATH +COPY ./dev-requirements.txt . +RUN pip3 install -r dev-requirements.txt + +WORKDIR $APP_SRC_PATH \ No newline at end of file diff --git a/README.md b/README.md index 2039e128de70ba0a25157f2f7f9a2a7bf542bcbd..fb280d5abbb0ef0fb6f22be4c15eb31d726b0bf4 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,55 @@ --- -title: Nfl Playoff Challenge Streamlit -emoji: šŸ˜» -colorFrom: pink -colorTo: gray +title: YFDash +emoji: šŸƒ +colorFrom: indigo +colorTo: blue sdk: streamlit sdk_version: 1.29.0 -app_file: app.py +app_file: src/Home.py pinned: false --- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference + +# nfl-playoff-challenge-steamlit Streamlit Application + +Template Repo for Streamlit Application - https://github.com/JonSolow/streamlit-template + +Here are some of the features of the template: + +- VSCode .devcontainer for local development: [Documentation](https://code.visualstudio.com/docs/devcontainers/containers) + - docker-compose.yml + - Dockerfile +- Linting Configuration [TO-DO] (allows for clean code and quicker detection of possible bugs as one of the first steps in Shift-left testing) + - [Black](https://black.readthedocs.io/en/stable/index.html) + - [ruff](https://beta.ruff.rs/docs/) + - [mypy](https://mypy.readthedocs.io/en/stable/index.html) +- Unit Tests + - [pytest](https://docs.pytest.org/) + +# Start Here to Develop + +1. Prerequisites + +- Install [Visual Studio Code](https://code.visualstudio.com/) +- Install [Visual Studo Code Extension - Dev containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- Install [Docker](https://www.docker.com/) + +2. Confirm Docker is installed by executing command `docker -v` +3. Open VSCode to a New Window and open this repository's directory +4. You may see a notification that the Folder containers a Dev Container configuration file. If so, click on "Reopen in Container" + +- If you do not see this notification, press `F1` key and begin typing the following until you can see the option "Dev Containers: Rebuild and reopen in Container". +- This action will reopen the VSCode within a Docker container suitable to develop and locally run the application. + +5. The dev container will start up the Streamlit application. + +- To access the application, navigate to http://localhost:8501 + - The container forwards the port 8501 where the Streamlit application is hosted +- Any changes made to the code will be reflected in the Streamlit application when you refresh. + +6. Now inside the VSCode dev container, to run tests, execute `./tests/run_tests.sh` + +- This script has an optional argument `-f` for "fix mode" which allows for configuration of black and ruff to automatically apply fixes. + +7. As functions are added to the application, unit tests can/should be added in `tests/unit`, with existing support utilizing the `pytest` library. diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fffeab12f165383ca04ccd8ae7c838bc03c5b767 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,463 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --extra=dev --output-file=dev-requirements.txt pyproject.toml +# +altair==5.2.0 + # via streamlit +annotated-types==0.6.0 + # via pydantic +anyio==4.1.0 + # via + # httpcore + # jupyter-server +argon2-cffi==23.1.0 + # via jupyter-server +argon2-cffi-bindings==21.2.0 + # via argon2-cffi +arrow==1.3.0 + # via isoduration +asttokens==2.4.1 + # via stack-data +async-lru==2.0.4 + # via jupyterlab +attrs==23.1.0 + # via + # jsonschema + # referencing +babel==2.13.1 + # via jupyterlab-server +beautifulsoup4==4.12.2 + # via + # nfl-playoff-challenge-steamlit (pyproject.toml) + # nbconvert +black==23.11.0 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +bleach==6.1.0 + # via nbconvert +blinker==1.7.0 + # via streamlit +cachetools==5.3.2 + # via streamlit +certifi==2023.11.17 + # via + # httpcore + # httpx + # requests +cffi==1.16.0 + # via argon2-cffi-bindings +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # black + # streamlit +comm==0.2.0 + # via + # ipykernel + # ipywidgets +debugpy==1.8.0 + # via ipykernel +decorator==5.1.1 + # via ipython +defusedxml==0.7.1 + # via nbconvert +duckdb==0.9.2 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.0 + # via nbformat +fqdn==1.5.1 + # via jsonschema +gitdb==4.0.11 + # via gitpython +gitpython==3.1.40 + # via streamlit +h11==0.14.0 + # via httpcore +html5lib==1.1 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +httpcore==0.17.3 + # via httpx +httpx==0.24.1 + # via httpx-oauth +httpx-oauth==0.13.0 + # via streamlit-oauth +idna==3.6 + # via + # anyio + # httpx + # jsonschema + # requests +importlib-metadata==6.9.0 + # via streamlit +iniconfig==2.0.0 + # via pytest +ipykernel==6.27.1 + # via + # jupyter + # jupyter-console + # jupyterlab + # qtconsole +ipython==8.18.1 + # via + # ipykernel + # ipywidgets + # jupyter-console +ipywidgets==8.1.1 + # via jupyter +isoduration==20.11.0 + # via jsonschema +jedi==0.19.1 + # via ipython +jinja2==3.1.2 + # via + # altair + # jupyter-server + # jupyterlab + # jupyterlab-server + # nbconvert + # pydeck +json5==0.9.14 + # via jupyterlab-server +jsonpointer==2.4 + # via jsonschema +jsonschema[format-nongpl]==4.20.0 + # via + # altair + # jupyter-events + # jupyterlab-server + # nbformat +jsonschema-specifications==2023.11.2 + # via jsonschema +jupyter==1.0.0 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +jupyter-client==8.6.0 + # via + # ipykernel + # jupyter-console + # jupyter-server + # nbclient + # qtconsole +jupyter-console==6.6.3 + # via jupyter +jupyter-core==5.5.0 + # via + # ipykernel + # jupyter-client + # jupyter-console + # jupyter-server + # jupyterlab + # nbclient + # nbconvert + # nbformat + # qtconsole +jupyter-events==0.9.0 + # via jupyter-server +jupyter-lsp==2.2.1 + # via jupyterlab +jupyter-server==2.11.1 + # via + # jupyter-lsp + # jupyterlab + # jupyterlab-server + # notebook + # notebook-shim +jupyter-server-terminals==0.4.4 + # via jupyter-server +jupyterlab==4.0.9 + # via notebook +jupyterlab-pygments==0.3.0 + # via nbconvert +jupyterlab-server==2.25.2 + # via + # jupyterlab + # notebook +jupyterlab-widgets==3.0.9 + # via ipywidgets +lxml==4.9.3 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +lxml-stubs==0.4.0 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.3 + # via + # jinja2 + # nbconvert +matplotlib-inline==0.1.6 + # via + # ipykernel + # ipython +mdurl==0.1.2 + # via markdown-it-py +mistune==3.0.2 + # via nbconvert +mypy==1.7.1 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +mypy-extensions==1.0.0 + # via + # black + # mypy +nbclient==0.9.0 + # via nbconvert +nbconvert==7.11.0 + # via + # jupyter + # jupyter-server +nbformat==5.9.2 + # via + # jupyter-server + # nbclient + # nbconvert +nest-asyncio==1.5.8 + # via ipykernel +notebook==7.0.6 + # via jupyter +notebook-shim==0.2.3 + # via + # jupyterlab + # notebook +numpy==1.26.2 + # via + # nfl-playoff-challenge-steamlit (pyproject.toml) + # altair + # pandas + # pandas-stubs + # pyarrow + # pydeck + # streamlit +overrides==7.4.0 + # via jupyter-server +packaging==23.2 + # via + # altair + # black + # ipykernel + # jupyter-server + # jupyterlab + # jupyterlab-server + # nbconvert + # pytest + # qtconsole + # qtpy + # streamlit +pandas==2.1.3 + # via + # nfl-playoff-challenge-steamlit (pyproject.toml) + # altair + # streamlit +pandas-stubs==2.1.1.230928 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +pandocfilters==1.5.0 + # via nbconvert +parso==0.8.3 + # via jedi +pathspec==0.11.2 + # via black +pexpect==4.9.0 + # via ipython +pillow==10.1.0 + # via streamlit +platformdirs==4.0.0 + # via + # black + # jupyter-core +pluggy==1.3.0 + # via pytest +prometheus-client==0.19.0 + # via jupyter-server +prompt-toolkit==3.0.41 + # via + # ipython + # jupyter-console +protobuf==4.25.1 + # via streamlit +psutil==5.9.6 + # via ipykernel +ptyprocess==0.7.0 + # via + # pexpect + # terminado +pure-eval==0.2.2 + # via stack-data +pyarrow==14.0.1 + # via streamlit +pycparser==2.21 + # via cffi +pydantic==2.5.2 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +pydantic-core==2.14.5 + # via pydantic +pydeck==0.8.1b0 + # via streamlit +pygments==2.17.2 + # via + # ipython + # jupyter-console + # nbconvert + # qtconsole + # rich +pytest==7.4.3 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +python-dateutil==2.8.2 + # via + # arrow + # jupyter-client + # pandas + # streamlit +python-dotenv==1.0.0 + # via streamlit-oauth +python-json-logger==2.0.7 + # via jupyter-events +pytz==2023.3.post1 + # via pandas +pyyaml==6.0.1 + # via jupyter-events +pyzmq==25.1.1 + # via + # ipykernel + # jupyter-client + # jupyter-console + # jupyter-server + # qtconsole +qtconsole==5.5.1 + # via jupyter +qtpy==2.4.1 + # via qtconsole +referencing==0.31.1 + # via + # jsonschema + # jsonschema-specifications + # jupyter-events +requests==2.31.0 + # via + # jupyterlab-server + # streamlit +rfc3339-validator==0.1.4 + # via + # jsonschema + # jupyter-events +rfc3986-validator==0.1.1 + # via + # jsonschema + # jupyter-events +rich==13.7.0 + # via streamlit +rpds-py==0.13.2 + # via + # jsonschema + # referencing +ruff==0.1.6 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +send2trash==1.8.2 + # via jupyter-server +six==1.16.0 + # via + # asttokens + # bleach + # html5lib + # python-dateutil + # rfc3339-validator +smmap==5.0.1 + # via gitdb +sniffio==1.3.0 + # via + # anyio + # httpcore + # httpx +soupsieve==2.5 + # via beautifulsoup4 +stack-data==0.6.3 + # via ipython +streamlit==1.29.0 + # via + # nfl-playoff-challenge-steamlit (pyproject.toml) + # streamlit-oauth +streamlit-oauth==0.1.5 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +tenacity==8.2.3 + # via streamlit +terminado==0.18.0 + # via + # jupyter-server + # jupyter-server-terminals +tinycss2==1.2.1 + # via nbconvert +toml==0.10.2 + # via streamlit +toolz==0.12.0 + # via altair +tornado==6.4 + # via + # ipykernel + # jupyter-client + # jupyter-server + # jupyterlab + # notebook + # streamlit + # terminado +traitlets==5.14.0 + # via + # comm + # ipykernel + # ipython + # ipywidgets + # jupyter-client + # jupyter-console + # jupyter-core + # jupyter-events + # jupyter-server + # jupyterlab + # matplotlib-inline + # nbclient + # nbconvert + # nbformat + # qtconsole +types-beautifulsoup4==4.12.0.7 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +types-html5lib==1.1.11.15 + # via types-beautifulsoup4 +types-python-dateutil==2.8.19.14 + # via arrow +types-pytz==2023.3.1.1 + # via pandas-stubs +types-requests==2.31.0.10 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +typing-extensions==4.8.0 + # via + # mypy + # pydantic + # pydantic-core + # streamlit +tzdata==2023.3 + # via pandas +tzlocal==5.2 + # via streamlit +uri-template==1.3.0 + # via jsonschema +urllib3==2.1.0 + # via + # requests + # types-requests +validators==0.22.0 + # via streamlit +watchdog==3.0.0 + # via streamlit +wcwidth==0.2.12 + # via prompt-toolkit +webcolors==1.13 + # via jsonschema +webencodings==0.5.1 + # via + # bleach + # html5lib + # tinycss2 +websocket-client==1.6.4 + # via jupyter-server +widgetsnbextension==4.0.9 + # via ipywidgets +zipp==3.17.0 + # via importlib-metadata diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..9579b5cf3ccbee9802cb1f4ea8ee9bbadf5289d5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + streamlit: + build: + dockerfile: ./Dockerfile + context: ./ + target: service + ports: + - '8501:8501' + environment: + - USER_ID=1000 + - GROUP_ID=1000 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..7418c9b8042283a845b21237232fddd599854ea3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[build-system] +requires = ["setuptools"] + +[project] +requires-python = ">=3.9" +version = "1" +name = "nfl-playoff-challenge-steamlit" +dependencies = [ + "streamlit", + "beautifulsoup4", + "duckdb", + "html5lib", + "lxml", + "numpy", + "pandas", + "pydantic", + "streamlit-oauth", + ] + +[project.optional-dependencies] +dev = [ + "black", + "jupyter", + "lxml-stubs", + "mypy", + "pytest", + "pandas-stubs", + "ruff", + "types-beautifulsoup4", + "types-requests", + ] + +[tool.black] +line-length = 120 +target-version = ["py311"] + +[tool.ruff] +line-length = 120 +src = ["src"] + +[tool.mypy] +python_version = "3.11" + +[[tool.mypy.overrides]] +module = [ + 'streamlit_oauth' +] +ignore_missing_imports = true \ No newline at end of file diff --git a/regenerate_requirements.sh b/regenerate_requirements.sh new file mode 100755 index 0000000000000000000000000000000000000000..03c027e948470a0ac2a54ba7e5fa7a20c498f706 --- /dev/null +++ b/regenerate_requirements.sh @@ -0,0 +1,19 @@ + +#!/bin/bash + +set -ex + +ADDITIONAL_ARGS=$@ + +pip-compile \ + -o requirements.txt \ + $ADDITIONAL_ARGS \ + pyproject.toml + +pip-compile \ + --extra=dev \ + -o dev-requirements.txt \ + $ADDITIONAL_ARGS \ + pyproject.toml + +python update_streamlit_version.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e86c119eec6c859023293c85fc13039858e413f0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,167 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --output-file=requirements.txt pyproject.toml +# +altair==5.2.0 + # via streamlit +annotated-types==0.6.0 + # via pydantic +anyio==4.1.0 + # via httpcore +attrs==23.1.0 + # via + # jsonschema + # referencing +beautifulsoup4==4.12.2 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +blinker==1.7.0 + # via streamlit +cachetools==5.3.2 + # via streamlit +certifi==2023.11.17 + # via + # httpcore + # httpx + # requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via streamlit +duckdb==0.9.2 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +gitdb==4.0.11 + # via gitpython +gitpython==3.1.40 + # via streamlit +h11==0.14.0 + # via httpcore +html5lib==1.1 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +httpcore==0.17.3 + # via httpx +httpx==0.24.1 + # via httpx-oauth +httpx-oauth==0.13.0 + # via streamlit-oauth +idna==3.6 + # via + # anyio + # httpx + # requests +importlib-metadata==6.9.0 + # via streamlit +jinja2==3.1.2 + # via + # altair + # pydeck +jsonschema==4.20.0 + # via altair +jsonschema-specifications==2023.11.2 + # via jsonschema +lxml==4.9.3 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.3 + # via jinja2 +mdurl==0.1.2 + # via markdown-it-py +numpy==1.26.2 + # via + # nfl-playoff-challenge-steamlit (pyproject.toml) + # altair + # pandas + # pyarrow + # pydeck + # streamlit +packaging==23.2 + # via + # altair + # streamlit +pandas==2.1.3 + # via + # nfl-playoff-challenge-steamlit (pyproject.toml) + # altair + # streamlit +pillow==10.1.0 + # via streamlit +protobuf==4.25.1 + # via streamlit +pyarrow==14.0.1 + # via streamlit +pydantic==2.5.2 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +pydantic-core==2.14.5 + # via pydantic +pydeck==0.8.1b0 + # via streamlit +pygments==2.17.2 + # via rich +python-dateutil==2.8.2 + # via + # pandas + # streamlit +python-dotenv==1.0.0 + # via streamlit-oauth +pytz==2023.3.post1 + # via pandas +referencing==0.31.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via streamlit +rich==13.7.0 + # via streamlit +rpds-py==0.13.2 + # via + # jsonschema + # referencing +six==1.16.0 + # via + # html5lib + # python-dateutil +smmap==5.0.1 + # via gitdb +sniffio==1.3.0 + # via + # anyio + # httpcore + # httpx +soupsieve==2.5 + # via beautifulsoup4 +streamlit==1.29.0 + # via + # nfl-playoff-challenge-steamlit (pyproject.toml) + # streamlit-oauth +streamlit-oauth==0.1.5 + # via nfl-playoff-challenge-steamlit (pyproject.toml) +tenacity==8.2.3 + # via streamlit +toml==0.10.2 + # via streamlit +toolz==0.12.0 + # via altair +tornado==6.4 + # via streamlit +typing-extensions==4.8.0 + # via + # pydantic + # pydantic-core + # streamlit +tzdata==2023.3 + # via pandas +tzlocal==5.2 + # via streamlit +urllib3==2.1.0 + # via requests +validators==0.22.0 + # via streamlit +watchdog==3.0.0 + # via streamlit +webencodings==0.5.1 + # via html5lib +zipp==3.17.0 + # via importlib-metadata diff --git a/src/.streamlit/config.toml b/src/.streamlit/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..c9b8c45f3578a756e8c2bd7978828c781cdf29fe --- /dev/null +++ b/src/.streamlit/config.toml @@ -0,0 +1,2 @@ +[theme] +base="dark" \ No newline at end of file diff --git a/src/Home.py b/src/Home.py new file mode 100644 index 0000000000000000000000000000000000000000..edb6745d66963fa4fa19d1b2738fd4d0f6559591 --- /dev/null +++ b/src/Home.py @@ -0,0 +1,27 @@ +import streamlit as st + +from config import DEFAULT_ICON, LEAGUE_NAME +from login_component import get_authorization_button +from page_selector import remove_seasonal_pages + + +def get_app(): + keeper_title = f"{LEAGUE_NAME}" + st.set_page_config(page_title=keeper_title, page_icon=DEFAULT_ICON) + get_authorization_button() + st.markdown( + f""" + Welcome {LEAGUE_NAME}! + + Navigate between pages using the left sidebar. + + If the sidebar is not visible, click the **>** in the upper left corner to open. + + """ + ) + + remove_seasonal_pages() + + +if __name__ == "__main__": + get_app() diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000000000000000000000000000000000000..6f7537fedfb211a8e63ac0cc308172bafa7bcbfe --- /dev/null +++ b/src/config.py @@ -0,0 +1,3 @@ +LEAGUE_NAME = "LOFG" +DEFAULT_ICON = "šŸ‰" +LEAGUE_NUMBER_TEAMS = 12 diff --git a/src/domain/__init__.py b/src/domain/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/domain/conferences.py b/src/domain/conferences.py new file mode 100644 index 0000000000000000000000000000000000000000..37ad30515366643624dfff2eaea3d48513c13a71 --- /dev/null +++ b/src/domain/conferences.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass +from typing import List + + +@dataclass +class NFLConference: + name: str + short_name: str + + def __post_init__(self): + ALL_CONFERENCES.append(self) + + +ALL_CONFERENCES: List[NFLConference] = [] + +NFC = NFLConference(name="National Football Conference", short_name="NFC") +AFC = NFLConference(name="American Football Conference", short_name="AFC") diff --git a/src/domain/divisions.py b/src/domain/divisions.py new file mode 100644 index 0000000000000000000000000000000000000000..3db7c027b0c8c3f8f2f96a8546c09b76df28afb8 --- /dev/null +++ b/src/domain/divisions.py @@ -0,0 +1,24 @@ +from dataclasses import dataclass +from typing import List +from domain import conferences + + +@dataclass +class NFLDivision: + name: str + conference: conferences.NFLConference + + def __post_init__(self): + ALL_DIVISIONS.append(self) + + +ALL_DIVISIONS: List[NFLDivision] = [] + +NFCWest = NFLDivision(name="NFC West", conference=conferences.NFC) +NFCNorth = NFLDivision(name="NFC North", conference=conferences.NFC) +NFCSouth = NFLDivision(name="NFC South", conference=conferences.NFC) +NFCEast = NFLDivision(name="NFC East", conference=conferences.NFC) +AFCWest = NFLDivision(name="AFC West", conference=conferences.AFC) +AFCNorth = NFLDivision(name="AFC North", conference=conferences.AFC) +AFCSouth = NFLDivision(name="AFC South", conference=conferences.AFC) +AFCEast = NFLDivision(name="AFC East", conference=conferences.AFC) diff --git a/src/domain/teams.py b/src/domain/teams.py new file mode 100644 index 0000000000000000000000000000000000000000..5df64f8f5be7e12ea38091902836f39fd871ac52 --- /dev/null +++ b/src/domain/teams.py @@ -0,0 +1,286 @@ +from dataclasses import dataclass +from urllib.parse import urljoin +from typing import List +from domain.conferences import NFLConference +from domain import divisions + + +@dataclass +class NFLTeam: + team_name: str + team_short_name: str + city: str + division: divisions.NFLDivision + footballguys_short_name: str = "" + url: str = "" + injury_report_suffix: str = "/team/injury-report/" + + def __post_init__(self): + self.footballguys_short_name = ( + self.team_short_name if self.footballguys_short_name == "" else self.footballguys_short_name + ) + self.conference: NFLConference = self.division.conference + self.injury_report_url = urljoin(self.url, self.injury_report_suffix) + self.team_full_name = " ".join([self.city, self.team_name]) + + ALL_TEAMS.append(self) + + +ALL_TEAMS: List[NFLTeam] = [] + + +arizona_cardinals = NFLTeam( + team_name="Cardinals", + team_short_name="ARI", + city="Arizona", + division=divisions.NFCWest, + url="https://www.azcardinals.com/", +) + +atlanta_falcons = NFLTeam( + team_name="Falcons", + team_short_name="ATL", + city="Atlanta", + division=divisions.NFCSouth, + url="https://www.atlantafalcons.com/", +) + +baltimore_ravens = NFLTeam( + team_name="Ravens", + team_short_name="BAL", + city="Baltimore", + division=divisions.AFCNorth, + url="https://www.baltimoreravens.com/", +) + +buffalo_bills = NFLTeam( + team_name="Bills", + team_short_name="BUF", + city="Buffalo", + division=divisions.AFCEast, + url="https://www.buffalobills.com/", +) + +carolina_panthers = NFLTeam( + team_name="Panthers", + team_short_name="CAR", + city="Carolina", + division=divisions.NFCSouth, + url="https://www.panthers.com/", +) + +chicago_bears = NFLTeam( + team_name="Bears", + team_short_name="CHI", + city="Chicago", + division=divisions.NFCNorth, + url="https://www.chicagobears.com/", +) + +cincinnati_bengals = NFLTeam( + team_name="Bengals", + team_short_name="CIN", + city="Cincinnati", + division=divisions.AFCNorth, + url="https://www.bengals.com/", +) + +cleveland_browns = NFLTeam( + team_name="Browns", + team_short_name="CLE", + city="Cleveland", + division=divisions.AFCNorth, + url="https://www.clevelandbrowns.com/", +) + +dallas_cowboys = NFLTeam( + team_name="Cowboys", + team_short_name="DAL", + city="Dallas", + division=divisions.NFCEast, + url="https://www.dallascowboys.com/", +) + +denver_broncos = NFLTeam( + team_name="Broncos", + team_short_name="DEN", + city="Denver", + division=divisions.AFCWest, + url="https://www.denverbroncos.com/", +) + +detroit_lions = NFLTeam( + team_name="Lions", + team_short_name="DET", + city="Detroit", + division=divisions.NFCNorth, + url="https://www.detroitlions.com/", +) + +green_bay_packers = NFLTeam( + team_name="Packers", + team_short_name="GB", + city="Green Bay", + division=divisions.NFCNorth, + url="https://www.packers.com/", +) + +houston_texans = NFLTeam( + team_name="Texans", + team_short_name="HOU", + city="Houston", + division=divisions.AFCSouth, + url="https://www.houstontexans.com/", +) + +indianapolis_colts = NFLTeam( + city="Indianapolis", + team_name="Colts", + team_short_name="IND", + division=divisions.AFCSouth, + url="https://www.colts.com/", +) + +jacksonville_jaguars = NFLTeam( + city="Jacksonville", + team_name="Jaguars", + team_short_name="JAX", + division=divisions.AFCSouth, + url="https://www.jaguars.com/", +) + +kansas_city_chiefs = NFLTeam( + city="Kansas City", + team_name="Chiefs", + team_short_name="KC", + division=divisions.AFCWest, + url="https://www.chiefs.com/", +) + +las_vegas_raiders = NFLTeam( + city="Las Vegas", + team_name="Raiders", + team_short_name="LV", + division=divisions.AFCWest, + url="https://www.raiders.com/", +) + +los_angeles_chargers = NFLTeam( + city="Los Angeles", + team_name="Chargers", + team_short_name="LAC", + division=divisions.AFCWest, + url="https://www.chargers.com/", +) + +los_angeles_rams = NFLTeam( + city="Los Angeles", + team_name="Rams", + team_short_name="LAR", + division=divisions.NFCWest, + url="https://www.therams.com/", +) + +miami_dolphins = NFLTeam( + city="Miami", + team_name="Dolphins", + team_short_name="MIA", + division=divisions.AFCEast, + url="https://www.miamidolphins.com/", +) + +minnesota_vikings = NFLTeam( + city="Minnesota", + team_name="Vikings", + team_short_name="MIN", + division=divisions.NFCNorth, + url="https://www.vikings.com/", +) + +new_england_patriots = NFLTeam( + city="New England", + team_name="Patriots", + team_short_name="NE", + division=divisions.AFCEast, + url="https://www.patriots.com/", +) + +new_orleans_saints = NFLTeam( + city="New Orleans", + team_name="Saints", + team_short_name="NO", + division=divisions.NFCSouth, + url="https://www.neworleanssaints.com/", +) + +new_york_giants = NFLTeam( + city="New York", + team_name="Giants", + team_short_name="NYG", + division=divisions.NFCEast, + url="https://www.giants.com/", +) + +new_york_jets = NFLTeam( + city="New York", + team_name="Jets", + team_short_name="NYJ", + division=divisions.AFCEast, + url="https://www.newyorkjets.com/", +) + +philadelphia_eagles = NFLTeam( + city="Philadelphia", + team_name="Eagles", + team_short_name="PHI", + division=divisions.NFCEast, + url="https://www.philadelphiaeagles.com/", +) + +pittsburgh_steelers = NFLTeam( + city="Pittsburgh", + team_name="Steelers", + team_short_name="PIT", + division=divisions.AFCNorth, + url="https://www.steelers.com/", +) + +san_francisco_49ers = NFLTeam( + city="San Francisco", + team_name="49ers", + team_short_name="SF", + division=divisions.NFCWest, + url="https://www.49ers.com/", +) + +seattle_seahawks = NFLTeam( + city="Seattle", + team_name="Seahawks", + team_short_name="SEA", + division=divisions.NFCWest, + url="https://www.seahawks.com/", +) + +tampa_bay_buccaneers = NFLTeam( + city="Tampa Bay", + team_name="Buccaneers", + team_short_name="TB", + division=divisions.NFCSouth, + url="https://www.buccaneers.com/", +) + +tennessee_titans = NFLTeam( + city="Tennessee", + team_name="Titans", + team_short_name="TEN", + division=divisions.AFCSouth, + url="https://www.tennesseetitans.com/", +) + +washington_football_team = NFLTeam( + city="Washington", + team_name="Commanders", + team_short_name="WAS", + division=divisions.NFCEast, + url="https://www.commanders.com/", +) diff --git a/src/login_component.py b/src/login_component.py new file mode 100644 index 0000000000000000000000000000000000000000..cd3d750f42a76a951e033cbf58860119bf5d3961 --- /dev/null +++ b/src/login_component.py @@ -0,0 +1,53 @@ +import streamlit as st + +from streamlit_oauth import OAuth2Component +import os + +# # Load environment variables from .env file +# from dotenv import load_dotenv +# load_dotenv() + +# Set environment variables +AUTHORIZE_URL = os.environ.get("AUTHORIZE_URL") +TOKEN_URL = os.environ.get("TOKEN_URL") +REFRESH_TOKEN_URL = os.environ.get("REFRESH_TOKEN_URL") +REVOKE_TOKEN_URL = os.environ.get("REVOKE_TOKEN_URL") +CLIENT_ID = os.environ.get("CLIENT_ID") +CLIENT_SECRET = os.environ.get("CLIENT_SECRET") +REDIRECT_URI = os.environ.get("REDIRECT_URI") +SCOPE = os.environ.get("SCOPE") + +ENABLE_LOGIN = os.environ.get("ENABLE_LOGIN", False) + +# Create OAuth2Component instance +oauth2 = OAuth2Component(CLIENT_ID, CLIENT_SECRET, AUTHORIZE_URL, TOKEN_URL, REFRESH_TOKEN_URL, REVOKE_TOKEN_URL) + + +def is_token_in_session() -> bool: + return "token" in st.session_state + + +def get_authorization_button(): + if not ENABLE_LOGIN: + return + # Check if token exists in session state + if not is_token_in_session(): + # If not, show authorize button + result = oauth2.authorize_button("Login", REDIRECT_URI, SCOPE) + if result and "token" in result: + # If authorization successful, save token in session state + st.session_state.token = result.get("token") + st.rerun() + else: + # # If token exists in session state, allow logout + st.session_state["token"] + if st.button("Logout"): + del st.session_state.token + st.rerun() + # # If token exists in session state, show the token + # token = st.session_state["token"] + # if st.button("Refresh Token"): + # # If refresh token button is clicked, refresh the token + # token = oauth2.refresh_token(token) + # st.session_state.token = token + # st.rerun() diff --git a/src/maximum_roster_strategy/__init__.py b/src/maximum_roster_strategy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/maximum_roster_strategy/data_loader.py b/src/maximum_roster_strategy/data_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..96f64907af827738330155754aac0888ab1e237d --- /dev/null +++ b/src/maximum_roster_strategy/data_loader.py @@ -0,0 +1,19 @@ +import os +import pandas as pd + + +MRS_SHEET_ID = os.environ.get("MRS_SHEET_ID") + + +def get_google_sheet_data() -> pd.DataFrame: + return get_sheet_data(0) + + +def get_sheet_data(sheet_id: int = 0): + sheet_url = f"https://docs.google.com/spreadsheet/ccc?key={MRS_SHEET_ID}&output=csv&gid={sheet_id}" + df = pd.read_csv(sheet_url) + return df + + +def get_timeslot_labels() -> pd.DataFrame: + return get_sheet_data(1875906423) diff --git a/src/page_selector.py b/src/page_selector.py new file mode 100644 index 0000000000000000000000000000000000000000..e6faf67fef6777ad8788f7d8692b2c71286d5a53 --- /dev/null +++ b/src/page_selector.py @@ -0,0 +1,40 @@ +from streamlit.source_util import _on_pages_changed, get_pages + +# Adapted from https://discuss.streamlit.io/t/how-to-hide-all-pages-before-login/32508 +# Note this code is intended to remove pages at app load time, not based on login + +SEASON_MODE = "season" +OFFSEASON_MODE = "offseason" + +CURRENT_MODE = SEASON_MODE + +MAIN_PAGE_FILE = "Home.py" + +MODE_PAGE_EXCLUSION_MAP = { + SEASON_MODE: [ + "Keepers", + "ECR", + "League_Simulation", + "Keeper_Rules", + "Maximum_Roster_Strategy", + ], + OFFSEASON_MODE: [ + "Practice_Reports", + "League_Simulation", + "Maximum_Roster_Strategy", + ], +} + + +def remove_seasonal_pages(): + all_pages = get_pages(MAIN_PAGE_FILE) + pages_to_remove = MODE_PAGE_EXCLUSION_MAP[CURRENT_MODE] + + page_keys_to_remove = [] + for k, v in all_pages.items(): + if v["page_name"] in pages_to_remove: + page_keys_to_remove.append(k) + for k_remove in page_keys_to_remove: + del all_pages[k_remove] + + _on_pages_changed.send() diff --git a/src/pages/10_Player_News.py b/src/pages/10_Player_News.py new file mode 100644 index 0000000000000000000000000000000000000000..893b4dcbc2cc1e58b92e0d72a676eec7cd265012 --- /dev/null +++ b/src/pages/10_Player_News.py @@ -0,0 +1,53 @@ +import datetime +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.nbcsports.player_news import get_player_news_window_hours + + +@st.cache_data(ttl=60 * 60 * 24) +def load_data(): + data = get_player_news_window_hours(24) + teams_list = sorted(filter(None, data.Team.unique())) + position_list = data.Position.unique() + data_load_time_str = datetime.datetime.utcnow().strftime("%m/%d/%Y %I:%M %p") + return data, teams_list, position_list, data_load_time_str + + +def get_page(): + page_title = "Player News - Last 24 Hours" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + if st.button("Refresh Data"): + st.cache_data.clear() + data, teams_list, position_list, data_load_time_str = load_data() + st.write(f"Data loaded as of: {data_load_time_str} UTC") + + teams_selected = st.multiselect("Team:", teams_list, placeholder="Select a team to filter") or teams_list + + with st.container(): + filtered_data = data[(data.Team.isin(teams_selected))] + st.dataframe( + filtered_data, + hide_index=True, + height=35 * (len(filtered_data) + 1) + 12, + use_container_width=True, + column_order=[ + "Date/Time", + "Name", + "Headline", + "Team", + "Position", + ], + column_config={ + "Date/Time": st.column_config.DatetimeColumn(format="MM-DD HH:mm"), + "Team": st.column_config.TextColumn(width="small"), + }, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/11_Next_Gen_Stats.py b/src/pages/11_Next_Gen_Stats.py new file mode 100644 index 0000000000000000000000000000000000000000..4f7981473d2b3bf24e0ea76edb41ca92d4d2156b --- /dev/null +++ b/src/pages/11_Next_Gen_Stats.py @@ -0,0 +1,70 @@ +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from streamlit_filter import get_multiselect_for_df_column +from queries.nflverse.github_data import get_nextgen_stats, get_current_tables, SEASON + + +hide_columns = [ + "season", + "season_type", + "player_gsis_id", + "player_first_name", + "player_last_name", + "player_jersey_number", + "player_short_name", +] + + +def get_page(): + page_title = f"Next Gen Stats - {SEASON}" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + + stat_category = st.selectbox("Stat Category", ["Passing", "Rushing", "Receiving"]) + ngs_table_name = f"nextgen_stats_ngs_{stat_category.lower()}" + current_tables_list = get_current_tables() + + if ngs_table_name not in current_tables_list: + st.write("Data not loaded.") + st.write("Check loaded data [here](./Load_Data)") + return + data = get_nextgen_stats(SEASON, stat_category) + + season_or_week = st.selectbox("Season or Weekly Stats", ["Season", "Week"]) + if season_or_week == "Season": + data = data[data["week"] == 0] + data.drop(columns=["week"], inplace=True) + else: + data = data[data["week"] > 0] + week_selection = st.slider( + "Filter Week Range:", + min_value=data["week"].min(), + max_value=data["week"].max(), + value=(data["week"].min(), data["week"].max()), + step=1, + ) + data = data[data["week"].between(*week_selection)] + + data.drop(columns=hide_columns, inplace=True) + positions_selected = get_multiselect_for_df_column(data, "player_position") + teams_selected = get_multiselect_for_df_column(data, "team_abbr") + + data = data[(data["player_position"].isin(positions_selected) & data["team_abbr"].isin(teams_selected))] + + with st.container(): + filtered_data = data + st.dataframe( + filtered_data, + hide_index=True, + # height=35 * (len(filtered_data) + 1) + 12, + use_container_width=False, + column_config={}, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/1_Keepers.py b/src/pages/1_Keepers.py new file mode 100644 index 0000000000000000000000000000000000000000..613503b9afdef7d49f022c288a19f1c2bbff7c95 --- /dev/null +++ b/src/pages/1_Keepers.py @@ -0,0 +1,178 @@ +import os +import numpy as np +import pandas as pd +import streamlit as st + +from config import DEFAULT_ICON, LEAGUE_NAME, LEAGUE_NUMBER_TEAMS +from shared_page import common_page_config +from streamlit_filter import filter_dataframe + + +KEEPER_DATA_URL = "../../tests/mocks/2023_keepers.csv" +HEADSHOT_DATA_URL = "../../tests/mocks/2023_player_headshots.csv" + + +def load_player_ids() -> pd.DataFrame: + df = pd.read_csv(r"https://raw.githubusercontent.com/dynastyprocess/data/master/files/db_playerids.csv") + df["merge_id"] = df["yahoo_id"].combine_first(df["stats_id"]) + return df + + +def load_adp() -> pd.DataFrame: + df = pd.read_csv(r"https://raw.githubusercontent.com/dynastyprocess/data/master/files/db_fpecr_latest.csv") + df = df.loc[ + df.fp_page == "/nfl/rankings/ppr-superflex-cheatsheets.php", + [ + "yahoo_id", + "ecr", + "sd", + ], + ] + return df + + +def convert_ecr_to_round_val(ecr_float: float, round_offset: float = 1.0, pick_offset: float = -1.0) -> float: + # As a float, store pick 1 of round 1 as 1.0 + return round_offset + (ecr_float + pick_offset) / LEAGUE_NUMBER_TEAMS + + +def add_opinionated_keeper_value(df: pd.DataFrame): + # Manual Hack for overranking of backup QBs + df.loc[ + df["name"].isin( + [ + "Teddy Bridgewater", + "Davis Mills", + "Andy Dalton", + "Tyler Huntley", + "Mike White", + "Gardner Minshew", + "Colt McCoy", + "Sam Darnold", + "Carson Wentz", + "Trey Lance", + "Taylor Heinicke", + ] + ), + ["ecr"], + ] = np.nan + + df["ecr"] = df["ecr"].apply(convert_ecr_to_round_val) + # Convert sd without offset to show as pure pick diff + df["sd"] = df["sd"].apply(lambda x: convert_ecr_to_round_val(x, 0, 0)) + # assumes midround keeper + # fill -99 for players that are not ranked in ecr + df["value_keeper"] = (df["keeper_cost"] + 0.5 - df["ecr"]).fillna(-99) + + +@st.cache_data(ttl=60 * 60 * 24) +def load_data(): + data = pd.read_csv(os.path.join(os.path.dirname(__file__), KEEPER_DATA_URL), index_col=0) + # Hack to get position, replace with better position from yahoo api in future + data["position"] = data["eligible_positions"].apply(lambda x: eval(x)[0]) + data.columns = data.columns.str.lower() + teams_list = sorted(list(data["team_name"].unique())) + + # Merge player ids + df_player_ids = load_player_ids() + data = data.merge(df_player_ids, how="left", left_on="player_id", right_on="merge_id", suffixes=("", "_ids")) + + # Merge ADP + df_adp = load_adp() + data = data.merge(df_adp, how="left", left_on="player_id", right_on="yahoo_id", suffixes=("", "_adp")) + add_opinionated_keeper_value(data) + return data, teams_list + + +def filtered_keeper_dataframe(data: pd.DataFrame, teams_list: list[str]): + teams_selected = st.multiselect("Team:", teams_list, placeholder="Select a user team to filter") + teams_filter = data["team_name"].isin(teams_selected) if teams_selected else data["team_name"].isin(teams_list) + + eligible_options = [True, False] + is_eligible_selected = st.multiselect( + "Keeper Eligible:", eligible_options, placeholder="Select True to filter eligible only" + ) + eligible_filter = ( + data["eligible"].isin(is_eligible_selected) if is_eligible_selected else data["eligible"].isin(eligible_options) + ) + is_advanced = st.checkbox("Show Advanced View") + + id_cols = [ + "team_name", + "headshot_url", + "name", + ] + + id_cols_advanced = [ + "team", + "position", + ] + + cost_cols = [ + "keeper_cost", + "eligible", + ] + + cost_cols_advanced = [ + "years_eligible", + ] + + adp_cols: list[str] = [] + + adp_cols_advanced = [ + "ecr", + "value_keeper", + ] + + if is_advanced: + show_columns = id_cols + id_cols_advanced + cost_cols + cost_cols_advanced + adp_cols + adp_cols_advanced + else: + show_columns = id_cols + cost_cols + adp_cols + + data_with_filters_applied = data.loc[teams_filter & eligible_filter, show_columns] + + filtered_data = filter_dataframe(data_with_filters_applied) + st.dataframe( + filtered_data, + hide_index=True, + height=35 * (len(filtered_data) + 1) + 12, + use_container_width=True, + column_config={ + "team_name": st.column_config.TextColumn(label="League Team", help="Name of fantasy League team."), + "headshot_url": st.column_config.ImageColumn(label="", help="Player image"), + "name": st.column_config.TextColumn(label="Name", help="Player's name"), + "team": st.column_config.TextColumn(label="NFL Team"), + "position": st.column_config.TextColumn(label="Position", help="Player's position"), + "keeper_cost": st.column_config.NumberColumn( + label="Keeper Cost", help="Draft Round Cost to keep player. See Rules for details." + ), + "eligible": st.column_config.CheckboxColumn(label="Eligible", help="Is player eligible to be keeper?"), + "years_eligible": st.column_config.NumberColumn( + label="Years Eligible", + help="Number of further consecutive seasons player can be kept (subject to maximum of 2)", + ), + "ecr": st.column_config.NumberColumn( + label="ECR", + help="Player's average draft round.pick Expert Consensus Rank (ECR) for PPR - Superflex League", + ), + "value_keeper": st.column_config.NumberColumn( + label="Value Keeper", + help="Approx. number of draft rounds of keeper value vs ECR PPR - Superflex League", + ), + }, + ) + + +def get_keeper_app(): + keeper_title = f"{LEAGUE_NAME} Keeper Options" + st.set_page_config(page_title=keeper_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(keeper_title) + data, teams_list = load_data() + + with st.container(): + filtered_keeper_dataframe(data, teams_list) + + +if __name__ == "__main__": + get_keeper_app() diff --git a/src/pages/3_ECR.py b/src/pages/3_ECR.py new file mode 100644 index 0000000000000000000000000000000000000000..4feec5f4c009a70381cdd97135ba32c1dad2551f --- /dev/null +++ b/src/pages/3_ECR.py @@ -0,0 +1,88 @@ +import os +import pandas as pd +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config +from streamlit_filter import filter_dataframe + + +KEEPER_DATA_URL = "../../tests/mocks/2023_keepers.csv" +HEADSHOT_DATA_URL = "../../tests/mocks/2023_player_headshots.csv" + + +def load_adp() -> pd.DataFrame: + df = pd.read_csv(r"https://raw.githubusercontent.com/dynastyprocess/data/master/files/db_fpecr_latest.csv") + df["ranking_type"] = df["fp_page"].apply(lambda x: os.path.split(x)[-1].replace(".php", "")) + return df + + +@st.cache_data(ttl=60 * 60 * 24) +def load_data(): + # Merge ADP + data = load_adp() + ranking_type_list = sorted(list(data.ranking_type.unique())) + return data, ranking_type_list + + +def filtered_ecr_dataframe(data: pd.DataFrame, ranking_type_list: list[str]): + default_ix = ranking_type_list.index("ppr-superflex-cheatsheets") + ranking_type_selected = st.selectbox("ECR Format:", ranking_type_list, index=default_ix) + ranking_type_filter = data["ranking_type"] == ranking_type_selected + + is_advanced = st.checkbox("Show Advanced View") + + id_cols = [ + # "player_square_image_url", + "player", + "pos", + "team", + ] + + id_cols_advanced = [ + "bye", + "player_owned_yahoo", + ] + + adp_cols: list[str] = [ + "ecr", + ] + + adp_cols_advanced = ["sd", "best", "worst"] + + if is_advanced: + show_columns = id_cols + id_cols_advanced + adp_cols + adp_cols_advanced + else: + show_columns = id_cols + adp_cols + + data_filtered_by_ranking_type = data.loc[ranking_type_filter] + latest_scrape_date = data_filtered_by_ranking_type.scrape_date.max() + st.write(f"Scraped data as of: {latest_scrape_date}") + + filtered_data = filter_dataframe(data.loc[ranking_type_filter, show_columns]) + st.dataframe( + filtered_data, + hide_index=True, + height=35 * (len(filtered_data) + 1) + 12, + use_container_width=True, + column_config={ + # "player_square_image_url": st.column_config.ImageColumn(label="", help="Player image"), + }, + ) + + st.write("Source: https://github.com/dynastyprocess/data") + + +def get_keeper_app(): + keeper_title = "Expert Consensus Rankings" + st.set_page_config(page_title=keeper_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(keeper_title) + data, ecr_type_list = load_data() + + with st.container(): + filtered_ecr_dataframe(data, ecr_type_list) + + +if __name__ == "__main__": + get_keeper_app() diff --git a/src/pages/4_Practice_Reports.py b/src/pages/4_Practice_Reports.py new file mode 100644 index 0000000000000000000000000000000000000000..14a1fe495e3dddd00b6201d3e324399ffe29c628 --- /dev/null +++ b/src/pages/4_Practice_Reports.py @@ -0,0 +1,66 @@ +import datetime +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.nfl_teams.practice_reports import scrape_all_team_injury_report, CURRENT_SEASON, CURRENT_WEEK +from streamlit_filter import filter_dataframe + + +@st.cache_data(ttl=60 * 60 * 1) +def load_data(): + data = scrape_all_team_injury_report() + teams_list = list(data.Team.unique()) + position_list = list(data.Position.unique()) + status_list = list(data.game_status.unique()) + last_practice_day_list = list(data["Last Practice Day"].unique()) + data_load_time_str = datetime.datetime.utcnow().strftime("%m/%d/%Y %I:%M %p") + return data, teams_list, position_list, status_list, last_practice_day_list, data_load_time_str + + +def get_page(): + page_title = f"Team Practice Reports - {CURRENT_SEASON} Week {CURRENT_WEEK}" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + if st.button("Refresh Data"): + st.cache_data.clear() + data, teams_list, position_list, status_list, last_practice_day_list, data_load_time_str = load_data() + st.write(f"Data loaded as of: {data_load_time_str} UTC") + teams_selected = st.multiselect("Team:", teams_list, placeholder="Select a team to filter") or teams_list + positions_selected = ( + st.multiselect("Position:", position_list, placeholder="Select a position to filter") or position_list + ) + status_selected = ( + st.multiselect("Game Status:", status_list, placeholder="Select a game status to filter") or status_list + ) + last_practice_day_selected = ( + st.multiselect( + "Last Practice Day:", last_practice_day_list, placeholder="Select a day of last team practice to filter" + ) + or last_practice_day_list + ) + + with st.container(): + filtered_data = filter_dataframe( + data[ + ( + data.Team.isin(teams_selected) + & data.Position.isin(positions_selected) + & data.game_status.isin(status_selected) + & data["Last Practice Day"].isin(last_practice_day_selected) + ) + ] + ) + st.dataframe( + filtered_data, + hide_index=True, + height=35 * (len(filtered_data) + 1) + 12, + use_container_width=False, + column_config={}, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/50_League_Simulation.py b/src/pages/50_League_Simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..a5eb96f046db4d6245bb9846ad3dd371e76fd3c7 --- /dev/null +++ b/src/pages/50_League_Simulation.py @@ -0,0 +1,26 @@ +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from login_component import is_token_in_session + + +def get_page(): + page_title = "Yahoo FF League Simulation" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + + if not is_token_in_session(): + st.write( + "You must authorize the application to access your account in order to use this feature." + " Please click Login button above." + ) + + else: + st.write("Logged in. Feature to go here") + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/5_Targets.py b/src/pages/5_Targets.py new file mode 100644 index 0000000000000000000000000000000000000000..8b0f0c6d147beacf859aff330a331e0d4bf3dffa --- /dev/null +++ b/src/pages/5_Targets.py @@ -0,0 +1,56 @@ +import datetime +import numpy as np +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.footballguys.constants import YEAR +from queries.footballguys.refresh import request_stat +from streamlit_filter import filter_dataframe + + +@st.cache_data(ttl=60 * 60 * 24) +def load_data(): + stat_name = "targets" + data = request_stat(stat_name) + data_load_time_str = datetime.datetime.utcnow().strftime("%m/%d/%Y %I:%M %p") + return data, data_load_time_str + + +def get_page(): + page_title = f"Player Targets - {YEAR}" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + if st.button("Refresh Data"): + st.cache_data.clear() + data, data_load_time_str = load_data() + st.write(f"Data loaded as of: {data_load_time_str} UTC") + + selected_subtotals = st.selectbox("Show:", ["Player Totals", "Position Totals"], index=0) + if selected_subtotals == "Player Totals": + data = data[~data.name.str.contains(" Totals")] + elif selected_subtotals == "Position Totals": + data = data[data.name.str.contains(" Totals")] + + value_types = st.selectbox("Counts / Percent:", ["Counts", "Percent"], index=0) + if value_types == "Percent": + numerical_data = data.select_dtypes(include=np.number) + numerical_cols = numerical_data.columns + df_percent_values = numerical_data / data.groupby("TEAM").transform(sum).select_dtypes(include=np.number) + data.loc[:, numerical_cols] = df_percent_values + + with st.container(): + filtered_data = filter_dataframe(data) + st.dataframe( + filtered_data, + hide_index=True, + height=35 * (len(filtered_data) + 1) + 12, + use_container_width=False, + column_config={}, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/6_Redzone_Opportunities.py b/src/pages/6_Redzone_Opportunities.py new file mode 100644 index 0000000000000000000000000000000000000000..3c892b4f5feca520b97d302d13c83d0b7458d1bb --- /dev/null +++ b/src/pages/6_Redzone_Opportunities.py @@ -0,0 +1,56 @@ +import datetime +import numpy as np +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.footballguys.constants import YEAR +from queries.footballguys.refresh import request_stat +from streamlit_filter import filter_dataframe + + +@st.cache_data(ttl=60 * 60 * 24) +def load_data(): + stat_name = "redzone" + data = request_stat(stat_name) + data_load_time_str = datetime.datetime.utcnow().strftime("%m/%d/%Y %I:%M %p") + return data, data_load_time_str + + +def get_page(): + page_title = f"Player Redzone Opportunities - {YEAR}" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + if st.button("Refresh Data"): + st.cache_data.clear() + data, data_load_time_str = load_data() + st.write(f"Data loaded as of: {data_load_time_str} UTC") + + selected_subtotals = st.selectbox("Show:", ["Player Totals", "Position Totals"], index=0) + if selected_subtotals == "Player Totals": + data = data[~data.name.str.contains(" Totals")] + elif selected_subtotals == "Position Totals": + data = data[data.name.str.contains(" Totals")] + + value_types = st.selectbox("Counts / Percent:", ["Counts", "Percent"], index=0) + if value_types == "Percent": + numerical_data = data.select_dtypes(include=np.number) + numerical_cols = numerical_data.columns + df_percent_values = numerical_data / data.groupby("TEAM").transform(sum).select_dtypes(include=np.number) + data.loc[:, numerical_cols] = df_percent_values + + with st.container(): + filtered_data = filter_dataframe(data) + st.dataframe( + filtered_data, + hide_index=True, + height=35 * (len(filtered_data) + 1) + 12, + use_container_width=False, + column_config={}, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/7_Snap_Counts.py b/src/pages/7_Snap_Counts.py new file mode 100644 index 0000000000000000000000000000000000000000..4e82db1b75d2738b314872e2e1466b6c0364b533 --- /dev/null +++ b/src/pages/7_Snap_Counts.py @@ -0,0 +1,69 @@ +import datetime +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.footballguys.constants import YEAR +from queries.nflverse.github_data import get_snap_counts, get_current_tables, SEASON + + +def load_data(): + data = get_snap_counts(YEAR) + data = data[data.fantasy_position] + teams_list = sorted(data.team.unique()) + position_list = data.position.unique() + weeks_list = sorted(data.week.unique()) + data_load_time_str = datetime.datetime.utcnow().strftime("%m/%d/%Y %I:%M %p") + return data, teams_list, position_list, weeks_list, data_load_time_str + + +def get_page(): + page_title = f"Snap Counts and Percentages - {YEAR}" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + if f"snap_counts_snap_counts_{SEASON}" not in get_current_tables(): + st.write("Data not loaded.") + st.write("Check loaded data [here](./Load_Data)") + return + data, teams_list, position_list, weeks_list, data_load_time_str = load_data() + st.write(f"Data loaded as of: {data_load_time_str} UTC") + teams_selected = st.multiselect("Team:", teams_list, placeholder="Select a team to filter") or teams_list + positions_selected = ( + st.multiselect("Position:", position_list, placeholder="Select a position to filter") or position_list + ) + weeks_selected = st.multiselect("Week:", weeks_list, placeholder="Select a week to filter") or weeks_list + + with st.container(): + filtered_data = data[ + (data.team.isin(teams_selected) & data.position.isin(positions_selected) & data.week.isin(weeks_selected)) + ] + st.dataframe( + filtered_data, + hide_index=True, + # height=35 * (len(filtered_data) + 1) + 12, + use_container_width=False, + column_order=[ + "season", + "game_type", + "week", + "player", + "position", + "team", + "opponent", + "offense_snaps", + "offense_pct", + "defense_snaps", + "defense_pct", + "st_snaps", + "st_pct", + ], + column_config={ + "season": st.column_config.TextColumn(help="Year of NFL Season"), + }, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/80_Maximum_Roster_Strategy.py b/src/pages/80_Maximum_Roster_Strategy.py new file mode 100644 index 0000000000000000000000000000000000000000..ab6c78fddc9d9e53ecc2085e4717a00819014081 --- /dev/null +++ b/src/pages/80_Maximum_Roster_Strategy.py @@ -0,0 +1,165 @@ +import pandas as pd +import streamlit as st +import streamlit.components.v1 as components + + +from config import DEFAULT_ICON +from shared_page import common_page_config, get_local_style +from maximum_roster_strategy import data_loader + + +MINIMUM_WEEK = 6 +MAXIMUM_WEEK = 7 + +MIN_TIER = 1 +MAX_TIER = 4 + +POSITION_OPTIONS = ["RB", "WR", "TE", "QB"] + +POSITION_ABBR_FULL_NAME_MAP = { + "RB": "Running Backs", + "WR": "Wide Receivers", + "TE": "Tight Ends", + "QB": "Quarterbacks (Superflex / 2QB Leagues Only)", +} + + +@st.cache_data(ttl=5 * 60) +def load_data(): + return data_loader.get_google_sheet_data(), data_loader.get_timeslot_labels() + + +def get_player_grid_div(player_series: pd.Series) -> str: + player_notes = player_series["Hold Condition"] + if (outcome := player_series["Outcome"]) == "Drop": + player_class = "drop-player" + elif outcome == "Light Hold": + player_class = "light-hold-player" + elif outcome == "Hold": + player_class = "hold-player" + else: + player_class = "undetermined-player" + + if isinstance(player_weekly_note := player_series["Article Notes"], str): + player_notes += "

" + player_weekly_note + return f""" +
+ + {player_series["Formatted"]} + +

+ {player_notes} +

+
+""" + + +def get_time_slot_div(time_slot_list: list[str]) -> str: + code_str = "" + for time_slot_idx, time_slot in enumerate(time_slot_list): + code_str += f"""
{time_slot}
\n""" + return code_str + + +def get_tier_div(tier_str: str | int, tier_num: str | int) -> str: + return f"""
Tier {tier_str}
""" + + +def get_player_container(df_players: pd.DataFrame, slot_number: int | str) -> str: + if len(df_players) == 0: + player_code_str = "
" + else: + player_code_str = "\n".join(df_players.apply(get_player_grid_div, axis=1).tolist()) + return f"""
{player_code_str}
""" + + +def get_position_breakdown(df: pd.DataFrame, position_abbr: str, position_full_str: str, time_slots: list[str]): + with st.container(): + st.header(position_full_str) + df_pos = df[df["Position"] == position_abbr] + + grid_code_str = "" + grid_code_str += get_time_slot_div(time_slots) + + tier_list = list(range(MIN_TIER, MAX_TIER + 1)) + slot_number = 0 + for tier_idx, tier in enumerate(tier_list): + grid_code_str += get_tier_div(tier, tier_idx + 1) + for time_slot in time_slots: + df_tier_slot = df_pos[(df_pos["TimeSlotName"] == time_slot) & (df_pos["Tier"] == tier)] + slot_number += 1 + grid_code_str += get_player_container(df_tier_slot, slot_number) + + components.html( + f""" +{get_local_style()} +
+ {grid_code_str} +
+
+
Colors Legend: +
Drop Player
| +
Light Hold Player
| +
Strong Hold Player
+
+""", + height=1000, + scrolling=True, + ) + + +def get_page(): + page_title = "Maximum Roster Strategy" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + + with st.expander(label="Instructions"): + st.write( + """ +To get started with MRS: https://solowfantasyfootball.wordpress.com/2023/09/07/maximum-roster-strategy-explained/ + +Players are organized by game time slot, position, and tier. + +Pick up a player during their game's time slot for potential upside if particular circumstances are met. + +After the game, players will be colored by outcome: Drop (Red), Light Hold (Yellow), or Strong Hold (Green).""" + ) + col_select, week_select = st.columns(2, gap="small") + url_params = st.experimental_get_query_params() + initial_position_index = 0 + if url_position := url_params.get("position"): + selected_position = url_position[0] + if selected_position in POSITION_OPTIONS: + initial_position_index = POSITION_OPTIONS.index(selected_position) + + week_options = list(range(MAXIMUM_WEEK, MINIMUM_WEEK - 1, -1)) + initial_week_index = 0 + if url_week := url_params.get("week"): + try: + selected_week = int(url_week[0]) + except Exception: + st.warning("Week parameter must be integer value", icon="āš ļø") + selected_week = MAXIMUM_WEEK + if selected_week in week_options: + initial_week_index = week_options.index(selected_week) + + with col_select: + position = st.selectbox(label="Position", options=POSITION_OPTIONS, index=initial_position_index) + with week_select: + week = st.selectbox(label="Week", options=week_options, index=initial_week_index) + url_params.update({"position": position, "week": week}) + st.experimental_set_query_params(**url_params) + if st.experimental_get_query_params().get("refresh"): + st.cache_data.clear() + df_mrs, all_time_slots_df = load_data() + df_mrs = df_mrs[df_mrs["Week"] == week] + current_week_timeslots = ( + all_time_slots_df[all_time_slots_df["Week"] == week].sort_values("WeekTimeSlotIndex").TimeSlotName.tolist() + ) + + get_position_breakdown(df_mrs, position, POSITION_ABBR_FULL_NAME_MAP[position], current_week_timeslots) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/8_FTN_Charting.py b/src/pages/8_FTN_Charting.py new file mode 100644 index 0000000000000000000000000000000000000000..3c8699291363dce55f76462d03344e69696e3353 --- /dev/null +++ b/src/pages/8_FTN_Charting.py @@ -0,0 +1,40 @@ +import datetime +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.footballguys.constants import YEAR +from queries.nflverse.github_data import get_ftn_charting, get_current_tables, SEASON + + +def load_data(): + data = get_ftn_charting(YEAR) + data_load_time_str = datetime.datetime.utcnow().strftime("%m/%d/%Y %I:%M %p") + return data, data_load_time_str + + +def get_page(): + page_title = f"FTN Charting - {YEAR}" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + if f"ftn_charting_ftn_charting_{SEASON}" not in get_current_tables(): + st.write("Data not loaded.") + st.write("Check loaded data [here](./Load_Data)") + return + data, data_load_time_str = load_data() + st.write(f"Data loaded as of: {data_load_time_str} UTC") + + with st.container(): + filtered_data = data + st.dataframe( + filtered_data, + hide_index=True, + # height=35 * (len(filtered_data) + 1) + 12, + use_container_width=False, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/98_Load_Data.py b/src/pages/98_Load_Data.py new file mode 100644 index 0000000000000000000000000000000000000000..837184794fc890c8690d22703b2e4a94c7b628d1 --- /dev/null +++ b/src/pages/98_Load_Data.py @@ -0,0 +1,35 @@ +import duckdb +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.nflverse.github_data import load_assets, get_current_tables + + +def get_page(): + page_title = "Data Loader" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + + current_tables_list = get_current_tables() + + if st.button("Refresh Data"): + load_assets() + st.rerun() + + if selected_table := st.selectbox("Describe a table:", current_tables_list, index=0): + describe_df = duckdb.sql(f"DESCRIBE {selected_table}").df() + st.dataframe( + describe_df, + hide_index=True, + use_container_width=True, + ) + + if st.checkbox("Explore data"): + st.dataframe(duckdb.sql(f"SELECT * FROM {selected_table} LIMIT 50").df()) + + +if __name__ == "__main__": + get_page() diff --git a/src/pages/99_Keeper_Rules.py b/src/pages/99_Keeper_Rules.py new file mode 100644 index 0000000000000000000000000000000000000000..21ac9ee78894c0030b676e215ca4e030673e9157 --- /dev/null +++ b/src/pages/99_Keeper_Rules.py @@ -0,0 +1,37 @@ +import streamlit as st + +from config import DEFAULT_ICON, LEAGUE_NAME +from shared_page import common_page_config + + +page_title = f"{LEAGUE_NAME} Keeper Rules" +st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON) +common_page_config() +st.title(page_title) +st.markdown( + """ +***Note: First LOFG keeper season was 2019*** + +1. Teams can keep up to 3 players from the prior season. + - Note: changed from 2 to 3 beginning with 2022 season +2. Players are kept at the expense of a draft pick. + - The round of that draft pick will depend on when the player was drafted in the prior season. +3. Players drafted in the 1st round of the prior season will not be eligible to keep. +4. Players drafted in rounds 2 and after can be kept at the expense of one higher round + - Ex: player drafted in round 2 can be kept for your 1st round pick. +5. Players who were not drafted and were picked up during the season can be kept for 10th round picks (FA Keeper cost) + - Changed beginning 2022 from 9th round +6. Post-Draft Acquired Keeper cost Minimum - + - Players drafted by other teams have a keeper cost of minimum of round player drafted and FA Keeper Cost. + - This rewards people who drafted the players, versus those who picked them up off waivers. +7. If you select 2 players with the same keeper cost, one of the players would count as one round higher. + - Ex: Two players both have keeper cost of 10th round. + - One of those players would instead be counted as your 9th round pick if both are kept. +8. Players can not be kept for more than 2 consecutive seasons + - Ex: player drafted in 2019 in 6th round, 2020 for 5th round, and 2021 for 4th round - can't be kept in 2022 + - Exception to the above rule is that players originally drafted in 2nd round can only be kept for one season. +9. Players traded in a season are not subject to the FA Keeper cost minimum cost +In other words, keeper rights transfer with the trade + + """ +) diff --git a/src/pages/9_Team_Formations.py b/src/pages/9_Team_Formations.py new file mode 100644 index 0000000000000000000000000000000000000000..81d6258a8de74cc0856412efbedfa9ee40212a3a --- /dev/null +++ b/src/pages/9_Team_Formations.py @@ -0,0 +1,83 @@ +import datetime +import pandas as pd +import streamlit as st + +from config import DEFAULT_ICON +from shared_page import common_page_config + +from queries.footballguys.constants import YEAR +from queries.nflverse.github_data import get_pbp_participation, get_current_tables, SEASON + + +def load_data(): + data = get_pbp_participation(YEAR) + teams_list = sorted(filter(None, data.possession_team.unique())) + # position_list = data.position.unique() + # weeks_list = sorted(data.week.unique()) + data_load_time_str = datetime.datetime.utcnow().strftime("%m/%d/%Y %I:%M %p") + return data, teams_list, data_load_time_str + + +def get_page(): + page_title = f"Team Formations - {YEAR}" + st.set_page_config(page_title=page_title, page_icon=DEFAULT_ICON, layout="wide") + common_page_config() + st.title(page_title) + if f"ftn_charting_ftn_charting_{SEASON}" not in get_current_tables(): + st.write("Data not loaded.") + st.write("Check loaded data [here](./Load_Data)") + return + data, teams_list, data_load_time_str = load_data() + st.write(f"Data loaded as of: {data_load_time_str} UTC") + default_groups = [ + "down", + "play_type", + "offense_personnel", + ] + group_options = [ + "week", + "down", + "qtr", + "ydstogo", + "play_type", + "pass_length", + "pass_location", + "possession_team", + "offense_formation", + "offense_personnel", + "number_of_pass_rushers", + "defenders_in_box", + "defense_personnel", + ] + group_by_selected = st.multiselect("Group by:", group_options) or default_groups + team_selected = st.selectbox("Team:", teams_list) + week_selection = st.slider( + "Filter Week Range:", + min_value=data["week"].min(), + max_value=data["week"].max(), + value=(data["week"].min(), data["week"].max()), + step=1, + ) + + with st.container(): + filtered_data = data[ + (data.possession_team == team_selected) + & (data.play_type.isin(["pass", "run"])) + & (data["week"].between(*week_selection)) + ] + + st.dataframe( + pd.pivot_table( + filtered_data, + values="count_col", + index=group_by_selected, + columns="week", + aggfunc={"count_col": "sum"}, + # margins=True, + ), + use_container_width=False, + ) + + +if __name__ == "__main__": + get_page() diff --git a/src/queries/__init__.py b/src/queries/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/queries/footballguys/__init__.py b/src/queries/footballguys/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/queries/footballguys/constants.py b/src/queries/footballguys/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..721a69b447bfe7aa4ab11e358c18e3db4445117f --- /dev/null +++ b/src/queries/footballguys/constants.py @@ -0,0 +1,34 @@ +from typing import List, Mapping + +# constants relevant to parsing from footballguys + +SNAP_PAGE_POSITON_ORDER: List[str] = [ + "QB", + "RB", + "WR", + "TE", + "DT", + "DE", + "ILB", + "OLB", + "CB", + "S", +] + +POSITIONS_TO_OFFENSE_DEFENSE: Mapping[str, str] = { + "QB": "OFF", + "RB": "OFF", + "WR": "OFF", + "TE": "OFF", + "DT": "DEF", + "DE": "DEF", + "ILB": "DEF", + "OLB": "DEF", + "S": "DEF", + "CB": "DEF", +} + + +BASE_URL = "https://www.footballguys.com/stats" + +YEAR = 2023 diff --git a/src/queries/footballguys/helpers.py b/src/queries/footballguys/helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..7ee73fa9661d3d82f17c5919701ee805ca7f92a4 --- /dev/null +++ b/src/queries/footballguys/helpers.py @@ -0,0 +1,130 @@ +from io import StringIO +import lxml.html +import pandas as pd +import requests +from typing import List +from queries.footballguys import constants as fbgc + + +def url_to_pandas(url) -> List[pd.DataFrame]: + page = requests.get(url) + table = pd.read_html(StringIO(page.text.replace("
", "-"))) + return table + + +def create_html_table_from_header_body(header_html_str: str, body_html_str: str): + return f""" + +{header_html_str} +{body_html_str} +
+""" + + +def extract_snaps_to_pandas(url: str): + root = lxml.html.document_fromstring(requests.get(url).text) + table_element_list = root.xpath("""//*[@id="stats_snapcounts_data"]/div/table""") + assert isinstance(table_element_list, list) + table_element = table_element_list[0] + assert isinstance(table_element, lxml.html.HtmlElement) + table_child_list = table_element.getchildren() + assert len(table_child_list) % 2 == 0 # check is even + half_len = int(len(table_child_list) / 2) + df_list = [] + for i in range(half_len): + table_html = create_html_table_from_header_body( + lxml.html.tostring(table_child_list[2 * i]), lxml.html.tostring(table_child_list[2 * i + 1]) + ).replace("\\n", "") + df = pd.read_html(table_html)[0] + # First column contains name and is initially labeled as each position, example "Quarterback" + # Insert column at front called POS and fill with current first column label + position_name = df.columns[0] + df.insert(0, "POS", position_name) + df.rename(columns={position_name: "name"}, inplace=True) + df_list.append(df) + return df_list + + +def add_snap_off_def_column(team_snap_df: pd.DataFrame): + off_def = team_snap_df["POS"].apply(lambda x: fbgc.POSITIONS_TO_OFFENSE_DEFENSE[x]) + team_snap_df.insert(0, "OFF/DEF", off_def) + + +def add_snap_position_column( + team_snap_df_list: List[pd.DataFrame], + position_name_array: List[str] = fbgc.SNAP_PAGE_POSITON_ORDER, +): + # blank player names between positions, so we can use cumsum + # 8/22/23 - We are currently failing here because snap counts are incorrectly not split by position atm + assert len(team_snap_df_list) == len(position_name_array) + for pos_idx, pos_df in enumerate(team_snap_df_list): + pos_df.insert(0, "POS", position_name_array[pos_idx]) + + +def set_multilevel_columns(df): + new_cols = [tuple(x.split("-")) if "-" in x else (x, x) for x in df.columns] + df.columns = pd.MultiIndex.from_tuples(new_cols) + + +def parse_snaps(team_short_name: str, base_url: str = fbgc.BASE_URL, year: int = fbgc.YEAR) -> pd.DataFrame: + print(f"Attempting to parse snaps for {team_short_name}") + team_snap_df_list = parse_team_page(team_short_name, base_url, "snap-counts", year) + team_snap_df = pd.concat(team_snap_df_list) + # add_snap_off_def_column(team_snap_df) + split_snap_count_percents(team_snap_df) + team_snap_df.dropna(subset=["name"], inplace=True) + # set_multilevel_columns(team_snap_df) + return team_snap_df + + +def add_targets_position(team_df: pd.DataFrame): + # fill blanks up by reversing index, fill down, and re-reversing + positions = team_df.name.apply(lambda x: x.replace(" Totals", "") if " Totals" in x else None)[::-1].ffill()[::-1] + team_df.insert(0, "POS", positions) + + +def parse_targets(team_short_name: str, base_url: str = fbgc.BASE_URL, year: int = fbgc.YEAR) -> pd.DataFrame: + # snaps are index 2 + print(f"Attempting to parse targets for {team_short_name}") + team_df = parse_team_page(team_short_name, base_url, "targets", year)[0] + add_targets_position(team_df) + return team_df[team_df.name.notna()] + + +def parse_redzone(team_short_name: str, base_url: str = fbgc.BASE_URL, year: int = fbgc.YEAR) -> pd.DataFrame: + # snaps are index 3 + print(f"Attempting to parse redzone for {team_short_name}") + team_df = parse_team_page(team_short_name, base_url, "redzone", year)[0] + add_targets_position(team_df) + return team_df[team_df.name.notna()] + + +def split_snap_count_percents(team_snap_df: pd.DataFrame): + for week in range(1, 18): + if f"Wk {week}" not in team_snap_df.columns: + continue + # if values are all NaN column will be dtype float 64 and should skip + if team_snap_df[f"Wk {week}"].dtype == float: + team_snap_df[f"{week}-count"] = 0 + team_snap_df[f"{week}-%"] = 0.0 + else: + week_split = team_snap_df[f"Wk {week}"].astype(str).str.split("-") + week_count = week_split.apply(lambda x: 0 if len(x) == 1 or x[0] == "" else int(x[0])) + week_pct = week_split.apply(lambda x: 0.0 if len(x) == 1 else float(x[1].strip("%")) / 100.0) + team_snap_df[f"{week}-count"] = week_count + team_snap_df[f"{week}-%"] = week_pct + team_snap_df.drop(columns=f"Wk {week}", inplace=True) + + +def parse_team_page( + team_short_name: str, + base_url: str, + stat_name: str, + year: int, +) -> List[pd.DataFrame]: + url = f"{base_url}/{stat_name}/teams?team={team_short_name}&year={year}" + if stat_name == "snap-counts": + all_tables = extract_snaps_to_pandas(url) + else: + all_tables = url_to_pandas(url) + return all_tables diff --git a/src/queries/footballguys/refresh.py b/src/queries/footballguys/refresh.py new file mode 100644 index 0000000000000000000000000000000000000000..1733f8a732258bda1c890071188d29dc19268bb3 --- /dev/null +++ b/src/queries/footballguys/refresh.py @@ -0,0 +1,46 @@ +from domain.teams import NFLTeam, ALL_TEAMS +from queries.footballguys.helpers import parse_snaps, parse_targets, parse_redzone +from typing import List, Callable, Optional +import pandas as pd + + +def add_team_name_columns(team_df: pd.DataFrame, team_short_name: str, team_name: str): + team_df.insert(0, "TEAM", team_short_name) + team_df.insert(1, "TEAM_NAME", team_name) + + +def apply_intended_column_sorting(df: pd.DataFrame, first_columns: List[str]) -> pd.DataFrame: + first_columns_in_df = [col for col in first_columns if col in df.columns] + remaining_columns = [col for col in df.columns if col not in first_columns_in_df] + return df[first_columns_in_df + remaining_columns] + + +def get_all_teams_stat_type( + all_teams_list: List[NFLTeam], + parsing_function: Callable, + store_key: str, + intended_first_columns: Optional[List[str]] = None, +): + team_df_list = [] + for team in all_teams_list: + team_df = parsing_function(team.footballguys_short_name) + add_team_name_columns(team_df, team.team_short_name, team.team_name) + team_df_list.append(team_df) + df = pd.concat(team_df_list) + if intended_first_columns: + df = apply_intended_column_sorting(df, intended_first_columns) + print(f"footballguy {store_key} loaded") + return df + + +def request_stat(stat_name: str) -> pd.DataFrame: + intended_col_sort = None + if stat_name == "targets": + parse_fxn = parse_targets + intended_col_sort = ["TEAM", "TEAM_NAME", "POS", "name", "total"] + elif stat_name == "snap-counts": + parse_fxn = parse_snaps + elif stat_name == "redzone": + parse_fxn = parse_redzone + intended_col_sort = ["TEAM", "TEAM_NAME", "POS", "name", "total"] + return get_all_teams_stat_type(ALL_TEAMS, parse_fxn, stat_name, intended_col_sort) diff --git a/src/queries/nbcsports/player_news.py b/src/queries/nbcsports/player_news.py new file mode 100644 index 0000000000000000000000000000000000000000..d4a45740211ccb9f20fae09dcc0bc9abf303db2e --- /dev/null +++ b/src/queries/nbcsports/player_news.py @@ -0,0 +1,52 @@ +from bs4 import BeautifulSoup +import datetime +import pandas as pd +import requests +from typing import Mapping + +NEWS_URL = "https://www.nbcsports.com/fantasy/football/player-news" + + +def find_soup_text_with_default(soup, element: str, find_search_map: Mapping[str, str]): + find_result = soup.find(element, find_search_map) + if not find_result: + return "" + return find_result.text.strip() + + +def parse_player_div(player_div): + return { + "Date/Time": player_div.find("div", {"class": "PlayerNewsPost-date"}).get("data-date"), + "Name": find_soup_text_with_default(player_div, "div", {"class": "PlayerNewsPost-name"}), + "Team": find_soup_text_with_default(player_div, "span", {"class": "PlayerNewsPost-team-abbr"}).upper(), + "Position": find_soup_text_with_default(player_div, "span", {"class": "PlayerNewsPost-position"}).title(), + "Headline": find_soup_text_with_default(player_div, "div", {"class": "PlayerNewsPost-headline"}), + "Analysis": find_soup_text_with_default(player_div, "div", {"class": "PlayerNewsPost-analysis"}), + } + + +def get_nfl_player_news(page_number: int = 1) -> pd.DataFrame: + url = f"{NEWS_URL}?p={page_number}" + request_page = requests.get(url) + soup = BeautifulSoup(request_page.content) + player_div_list = soup.find_all("div", {"class": "PlayerNewsPost"}) + if not player_div_list: + return pd.DataFrame() + parsed_player_list = [parse_player_div(d) for d in player_div_list] + df = pd.DataFrame(parsed_player_list) + df["Date/Time"] = pd.to_datetime(df["Date/Time"]) + return df + + +def get_player_news_window_hours(hours: int = 1): + end_date = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=hours) + page = 1 + max_pages = 20 + date_reached = False + df_list = [] + while page < max_pages and not date_reached: + last_news = get_nfl_player_news(page) + df_list.append(last_news) + date_reached = min(last_news["Date/Time"]) < end_date + page += 1 + return pd.concat(df_list) diff --git a/src/queries/nfl_teams/__init__.py b/src/queries/nfl_teams/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/queries/nfl_teams/practice_reports.py b/src/queries/nfl_teams/practice_reports.py new file mode 100644 index 0000000000000000000000000000000000000000..24968c328948a5e25ad90a2294ad80aae20d35b0 --- /dev/null +++ b/src/queries/nfl_teams/practice_reports.py @@ -0,0 +1,123 @@ +from bs4 import BeautifulSoup +import datetime +from multiprocessing import Pool +import numpy as np +import pandas as pd +from pydantic import BaseModel, Field +import requests +from typing import Optional +from urllib.parse import urljoin + +from domain.teams import ALL_TEAMS, NFLTeam + + +MULTIPROCESSING_ENABLED = False + +PRACTICE_WEEK = { + "Mon": 0, + "Tue": 1, + "Wed": 2, + "Thu": 3, + "Fri": 4, + "Sat": 5, + "Sun": 6, + "Monday": 0, + "Tuesday": 1, + "Wednesday": 2, + "Thursday": 3, + "Friday": 4, + "Saturday": 5, + "Sunday": 6, +} + + +DAY_OF_WEEK_STRING_MAPPING = { + "Monday": "Mon", + "Tuesday": "Tue", + "Wednesday": "Wed", + "Thursday": "Thu", + "Friday": "Fri", + "Saturday": "Sat", + "Sunday": "Sun", +} + + +WEEK_1_BEGIN_DATE = datetime.datetime(2023, 9, 4) +CURRENT_DATE = datetime.datetime.now() +CURRENT_WEEK = max(1, int(1 + (CURRENT_DATE - WEEK_1_BEGIN_DATE).days / 7)) +CURRENT_SEASON = 2023 + + +class PracticeReportRawRow(BaseModel): + Team: str + Player: str + Position: str + Injury: str + Sun: Optional[str] = None + Mon: Optional[str] = None + Tue: Optional[str] = None + Wed: Optional[str] = None + Thu: Optional[str] = None + Fri: Optional[str] = None + Sat: Optional[str] = None + game_status: str = Field(alias="Game Status") + + @classmethod + def replace_nan(self, value) -> str: + if isinstance(value, float): + if np.isnan(value): + return "" + return value + + @classmethod + def from_raw(cls, input_dict) -> "PracticeReportRawRow": + return cls(**{DAY_OF_WEEK_STRING_MAPPING.get(k, k): cls.replace_nan(v) for k, v in input_dict.items()}) + + +def get_injury_report_dataframe(team: NFLTeam): + injury_report_url = urljoin(team.injury_report_url, f"week/REG-{CURRENT_WEEK}") + report_request = requests.get(injury_report_url) + report_soup = BeautifulSoup(report_request.content) + team_names_spans = report_soup.find_all("span", {"class": "nfl-o-injury-report__club-name"}) + assert team_names_spans + team_names_str = [x.get_text() for x in team_names_spans] + assert team_names_str[0] == team.team_full_name + tables = report_soup.find_all("table") + df_report = pd.read_html(str(tables))[0] + return df_report + + +def scrape_team_injury_report(team: NFLTeam) -> pd.DataFrame: + print(f"Scraping Injury Report for: {team.team_full_name}") + try: + team_report = get_injury_report_dataframe(team) + except Exception: + print(f"Failed to scrape practice report for: {team.team_full_name}") + return pd.DataFrame() + validated_row_list = [] + for df_row_dict in team_report.to_dict("records"): + row_to_add = df_row_dict + row_to_add["Team"] = team.team_full_name + validated_row_list.append(PracticeReportRawRow.from_raw(row_to_add)) + validated_df = pd.DataFrame([x.dict() for x in validated_row_list]) + # drop all na columns + validated_df.dropna(axis=1, how="all", inplace=True) + # replace day of week with practice day from 1-3 + day_idx = 1 + last_practice_day = None + for col in validated_df.columns: + if col in PRACTICE_WEEK: + validated_df.rename(columns={col: str(day_idx)}, inplace=True) + day_idx += 1 + last_practice_day = col + validated_df["Last Practice Day"] = last_practice_day + return validated_df + + +def scrape_all_team_injury_report() -> pd.DataFrame: + if MULTIPROCESSING_ENABLED: + with Pool() as pool: + team_df_list = pool.map(scrape_team_injury_report, ALL_TEAMS) + else: + team_df_list = [scrape_team_injury_report(team) for team in ALL_TEAMS] + return pd.concat(team_df_list) diff --git a/src/queries/nflverse/__init__.py b/src/queries/nflverse/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/queries/nflverse/github_data.py b/src/queries/nflverse/github_data.py new file mode 100644 index 0000000000000000000000000000000000000000..5c21f83600ce502369bafc66407d0abe538b7e3e --- /dev/null +++ b/src/queries/nflverse/github_data.py @@ -0,0 +1,123 @@ +import duckdb +import pandas as pd +import os +from typing import Callable + + +duckdb.default_connection.execute("SET GLOBAL pandas_analyze_sample=100000") + +BASE_URL = "https://github.com/nflverse/nflverse-data/releases/download/" + + +FANTASY_POSITIONS = [ + "QB", + "RB", + "WR", + "TE", + "FB", + "K", +] + + +def get_snap_counts(season_int: int) -> pd.DataFrame: + df = duckdb.sql(f"SELECT * from snap_counts_snap_counts_{season_int}").df() + df["fantasy_position"] = df["position"].isin(FANTASY_POSITIONS) + return df + + +def get_play_by_play(season_int: int) -> pd.DataFrame: + df = duckdb.sql(f"SELECT * from pbp_play_by_play_{season_int}").df() + return df + + +def get_player_stats(season_int: int) -> pd.DataFrame: + df = duckdb.sql("SELECT * from player_stats_player_stats").df() + return df + + +def get_ftn_charting(season_int: int) -> pd.DataFrame: + df = duckdb.sql(f"SELECT * from ftn_charting_ftn_charting_{season_int}").df() + return df + + +def get_pbp_participation(season_int: int) -> pd.DataFrame: + df = duckdb.sql( + f""" + SELECT + a.* + , b.* + , 1 as count_col + from pbp_participation_pbp_participation_{season_int} a + left join pbp_play_by_play_{season_int} b + on a.play_id = b.play_id + and a.nflverse_game_id = b.game_id + where b.week is not null +""" + ).df() + return df + + +def get_nextgen_stats(season_int: int, stat_category: str) -> pd.DataFrame: + df = duckdb.sql(f"SELECT * from nextgen_stats_ngs_{stat_category} where season = {season_int}").df() + return df + + +SEASON = "2023" + +NFLVERSE_ASSETS = [ + ("ftn_charting", f"ftn_charting_{SEASON}.parquet"), + ("espn_data", "qbr_season_level.parquet"), + ("espn_data", "qbr_week_level.parquet"), + ("players", "players.parquet"), + ("pbp_participation", f"pbp_participation_{SEASON}.parquet"), + ("snap_counts", f"snap_counts_{SEASON}.parquet"), + ("player_stats", f"player_stats_{SEASON}.parquet"), + ("player_stats", f"player_stats_def_{SEASON}.parquet"), + ("player_stats", f"player_stats_kicking_{SEASON}.parquet"), + ("pfr_advstats", "advstats_season_def.parquet"), + ("pfr_advstats", "advstats_season_pass.parquet"), + ("pfr_advstats", "advstats_season_rec.parquet"), + ("pfr_advstats", "advstats_season_rush.parquet"), + ("pfr_advstats", f"advstats_week_def_{SEASON}.parquet"), + ("pfr_advstats", f"advstats_week_pass_{SEASON}.parquet"), + ("pfr_advstats", f"advstats_week_rec_{SEASON}.parquet"), + ("pfr_advstats", f"advstats_week_rush_{SEASON}.parquet"), + ("pbp", f"play_by_play_{SEASON}.parquet"), + ("nextgen_stats", "ngs_passing.parquet"), + ("nextgen_stats", "ngs_receiving.parquet"), + ("nextgen_stats", "ngs_rushing.parquet"), +] + + +class NflVerseDataAsset: + def __init__( + self, + release_tag: str, + asset_name: str, + dataframe_mutation_fxn: Callable[[pd.DataFrame], pd.DataFrame] = lambda x: x, + ): + self.release_tag = release_tag + self.asset_name = asset_name + self.dataframe_mutation_fxn = dataframe_mutation_fxn + self.table_name = f"{release_tag}_{asset_name.rsplit('.', 1)[0]}" + + def load_parquet_asset_to_df(self) -> pd.DataFrame: + location = os.path.join(BASE_URL, self.release_tag, self.asset_name) + df = pd.read_parquet(location) + return df + + def register_asset_to_duckdb(self) -> None: + df = self.load_parquet_asset_to_df() + df = self.dataframe_mutation_fxn(df) + duckdb.register(self.table_name, df) + + +def load_assets(): + for tag, asset in NFLVERSE_ASSETS: + asset = NflVerseDataAsset(tag, asset) + asset.register_asset_to_duckdb() + + +def get_current_tables() -> list[str]: + current_tables_df = duckdb.sql("SHOW TABLES").df() + return current_tables_df["name"].tolist() diff --git a/src/shared_page.py b/src/shared_page.py new file mode 100644 index 0000000000000000000000000000000000000000..acd77a420d0290b0948a60469db886604cf6e6ec --- /dev/null +++ b/src/shared_page.py @@ -0,0 +1,22 @@ +import os +import streamlit as st + +from page_selector import remove_seasonal_pages +from login_component import get_authorization_button + + +def get_local_style(): + code_str = "" + with open(os.path.join(os.path.dirname(__file__), "style.css")) as f: + code_str = "".format(f.read()) + return code_str + + +def local_css(): + return st.markdown(get_local_style(), unsafe_allow_html=True) + + +def common_page_config(): + local_css() + get_authorization_button() + remove_seasonal_pages() diff --git a/src/start.sh b/src/start.sh new file mode 100755 index 0000000000000000000000000000000000000000..3f020d6784c010b824ce7e0bcc19548bddb7b8fd --- /dev/null +++ b/src/start.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +streamlit run Home.py --server.port=8501 --server.address=0.0.0.0 diff --git a/src/streamlit_filter.py b/src/streamlit_filter.py new file mode 100644 index 0000000000000000000000000000000000000000..0a08037f7589c41aeb14731d68dd3f7183781221 --- /dev/null +++ b/src/streamlit_filter.py @@ -0,0 +1,102 @@ +# https://blog.streamlit.io/auto-generate-a-dataframe-filtering-ui-in-streamlit-with-filter_dataframe/ + +from pandas.api.types import ( + is_categorical_dtype, + is_datetime64_any_dtype, + is_numeric_dtype, + is_object_dtype, +) +import pandas as pd +import streamlit as st + + +def filter_dataframe(df: pd.DataFrame, force_on: bool = False, force_on_columns: list[str] = []) -> pd.DataFrame: + """ + Adds a UI on top of a dataframe to let viewers filter columns + + Args: + df (pd.DataFrame): Original dataframe + + Returns: + pd.DataFrame: Filtered dataframe + """ + if force_on: + modify = True + else: + modify = st.checkbox("Add more filters") + + if not modify: + return df + + df = df.copy() + + # Try to convert datetimes into a standard format (datetime, no timezone) + for col in df.columns: + if is_object_dtype(df[col]): + try: + df[col] = pd.to_datetime(df[col]) + except Exception: + pass + + if is_datetime64_any_dtype(df[col]): + df[col] = df[col].dt.tz_localize(None) + + modification_container = st.container() + + with modification_container: + to_filter_columns = st.multiselect("Filter dataframe on", df.columns) + force_on_columns + for column in to_filter_columns: + left, right = st.columns((1, 20)) + # Treat columns with < 17 unique values as categorical + if is_categorical_dtype(df[column]) or df[column].nunique() < 50: + user_cat_input = right.multiselect( + f"Values for {column}", + df[column].unique(), + default=list(df[column].unique()), + ) + df = df[df[column].isin(user_cat_input)] + elif is_numeric_dtype(df[column]): + _min = float(df[column].min()) + _max = float(df[column].max()) + step = (_max - _min) / 100 + user_num_input = right.slider( + f"Values for {column}", + min_value=_min, + max_value=_max, + value=(_min, _max), + step=step, + ) + df = df[df[column].between(*user_num_input)] + elif is_datetime64_any_dtype(df[column]): + user_date_input = right.date_input( + f"Values for {column}", + value=( + df[column].min(), + df[column].max(), + ), + ) + if isinstance(user_date_input, tuple): + if len(user_date_input) == 2: + user_date_input_dt = tuple(map(pd.to_datetime, user_date_input)) + start_date, end_date = user_date_input_dt + df = df.loc[df[column].between(start_date, end_date)] + else: + user_text_input = right.text_input( + f"Substring or regex in {column}", + ) + if user_text_input: + df = df[df[column].astype(str).str.contains(user_text_input)] + + return df + + +def get_multiselect_for_df_column(df: pd.DataFrame, column_name: str) -> list: + options_list = sorted(df[column_name].unique().tolist()) + if len(options_list) > 1: + selected = ( + st.multiselect(column_name.title(), options_list, placeholder=f"Select a {column_name} to filter") + or options_list + ) + else: + selected = options_list + return selected diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000000000000000000000000000000000000..79d1bd7f6c21e1ac9c72bacde7a08efcbadf54c2 --- /dev/null +++ b/src/style.css @@ -0,0 +1,150 @@ +.mrs-grid-player { + font-size: x-small; + margin-bottom: 5px; + padding-left: 4px; + padding-right: 4px; +} + +.tier { + text-align: center; +} + +.timeslot { + text-align: center; +} + +.tier1 { + border: 2px solid blue; +} +.tier2 { + border: 2px solid gold; +} + +.tier3 { + border: 2px solid silver; +} + +.tier4 { + border: 2px solid brown; +} + +.drop-player { + color: red; +} + +.light-hold-player { + color: yellow; +} + +.hold-player { + color: rgba(68, 235, 62, 0.774); +} + +.playerslot { + border-left: 1px solid white; + border-right: 1px solid white; +} + +.grid-legend { + color: white; + border: 1px solid white; + display: flex; + gap: 5px; + justify-content: center; + font-size: x-small; +} + +.grid-container-6 { + display: grid; + grid-template-columns: repeat(6, 1fr); + grid-template-rows: repeat(9, auto); + grid-column-gap: 0px; + grid-row-gap: 10px; + color: white; + + .timeslot1 { grid-area: 1 / 1 / 2 / 2; } + .timeslot2 { grid-area: 1 / 2 / 2 / 3; } + .timeslot3 { grid-area: 1 / 3 / 2 / 4; } + .timeslot4 { grid-area: 1 / 4 / 2 / 5; } + .timeslot5 { grid-area: 1 / 5 / 2 / 6; } + .timeslot6 { grid-area: 1 / 6 / 2 / 7; } + .tier1 { grid-area: 2 / 1 / 3 / 7; } + .playerslot1 { grid-area: 3 / 1 / 4 / 2; } + .playerslot2 { grid-area: 3 / 2 / 4 / 3; } + .playerslot3 { grid-area: 3 / 3 / 4 / 4; } + .playerslot4 { grid-area: 3 / 4 / 4 / 5; } + .playerslot5 { grid-area: 3 / 5 / 4 / 6; } + .playerslot6 { grid-area: 3 / 6 / 4 / 7; } + .tier2 { grid-area: 4 / 1 / 5 / 7; } + .playerslot7 { grid-area: 5 / 1 / 6 / 2; } + .playerslot8 { grid-area: 5 / 2 / 6 / 3; } + .playerslot9 { grid-area: 5 / 3 / 6 / 4; } + .playerslot10 { grid-area: 5 / 4 / 6 / 5; } + .playerslot11 { grid-area: 5 / 5 / 6 / 6; } + .playerslot12 { grid-area: 5 / 6 / 6 / 7; } + .tier3 { grid-area: 6 / 1 / 7 / 7; } + .playerslot13 { grid-area: 7 / 1 / 8 / 2; } + .playerslot14 { grid-area: 7 / 2 / 8 / 3; } + .playerslot15 { grid-area: 7 / 3 / 8 / 4; } + .playerslot16 { grid-area: 7 / 4 / 8 / 5; } + .playerslot17 { grid-area: 7 / 5 / 8 / 6; } + .playerslot18 { grid-area: 7 / 6 / 8 / 7; } + .tier4 { grid-area: 8 / 1 / 9 / 7; } + .playerslot19 { grid-area: 9 / 1 / 10 / 2; } + .playerslot20 { grid-area: 9 / 2 / 10 / 3; } + .playerslot21 { grid-area: 9 / 3 / 10 / 4; } + .playerslot22 { grid-area: 9 / 4 / 10 / 5; } + .playerslot23 { grid-area: 9 / 5 / 10 / 6; } + .playerslot24 { grid-area: 9 / 6 / 10 / 7; } + +} + +.grid-container-7 { + display: grid; + grid-template-columns: repeat(7, 1fr); + grid-template-rows: repeat(9, auto); + grid-column-gap: 0px; + grid-row-gap: 10px; + color: white; + + .timeslot1 { grid-area: 1 / 1 / 2 / 2; } + .timeslot2 { grid-area: 1 / 2 / 2 / 3; } + .timeslot3 { grid-area: 1 / 3 / 2 / 4; } + .timeslot4 { grid-area: 1 / 4 / 2 / 5; } + .timeslot5 { grid-area: 1 / 5 / 2 / 6; } + .timeslot6 { grid-area: 1 / 6 / 2 / 7; } + .timeslot7 { grid-area: 1 / 7 / 2 / 8; } + .tier1 { grid-area: 2 / 1 / 3 / 8; } + .playerslot1 { grid-area: 3 / 1 / 4 / 2; } + .playerslot2 { grid-area: 3 / 2 / 4 / 3; } + .playerslot3 { grid-area: 3 / 3 / 4 / 4; } + .playerslot4 { grid-area: 3 / 4 / 4 / 5; } + .playerslot5 { grid-area: 3 / 5 / 4 / 6; } + .playerslot6 { grid-area: 3 / 6 / 4 / 7; } + .playerslot7 { grid-area: 3 / 7 / 4 / 8; } + .tier2 { grid-area: 4 / 1 / 5 / 8; } + .playerslot8 { grid-area: 5 / 1 / 6 / 2; } + .playerslot9 { grid-area: 5 / 2 / 6 / 3; } + .playerslot10 { grid-area: 5 / 3 / 6 / 4; } + .playerslot11 { grid-area: 5 / 4 / 6 / 5; } + .playerslot12 { grid-area: 5 / 5 / 6 / 6; } + .playerslot13 { grid-area: 5 / 6 / 6 / 7; } + .playerslot14 { grid-area: 5 / 7 / 6 / 8; } + .tier3 { grid-area: 6 / 1 / 7 / 8; } + .playerslot15 { grid-area: 7 / 1 / 8 / 2; } + .playerslot16 { grid-area: 7 / 2 / 8 / 3; } + .playerslot17 { grid-area: 7 / 3 / 8 / 4; } + .playerslot18 { grid-area: 7 / 4 / 8 / 5; } + .playerslot19 { grid-area: 7 / 5 / 8 / 6; } + .playerslot20 { grid-area: 7 / 6 / 8 / 7; } + .playerslot21 { grid-area: 7 / 7 / 8 / 8; } + .tier4 { grid-area: 8 / 1 / 9 / 8; } + .playerslot22 { grid-area: 9 / 1 / 10 / 2; } + .playerslot23 { grid-area: 9 / 2 / 10 / 3; } + .playerslot24 { grid-area: 9 / 3 / 10 / 4; } + .playerslot25 { grid-area: 9 / 4 / 10 / 5; } + .playerslot26 { grid-area: 9 / 5 / 10 / 6; } + .playerslot27 { grid-area: 9 / 6 / 10 / 7; } + .playerslot28 { grid-area: 9 / 7 / 10 / 8; } + +} \ No newline at end of file diff --git a/tests/contract/test_nbcsports_player_news.py b/tests/contract/test_nbcsports_player_news.py new file mode 100644 index 0000000000000000000000000000000000000000..0d5c44e02c9b489b022d2939221b89f0d2ec101e --- /dev/null +++ b/tests/contract/test_nbcsports_player_news.py @@ -0,0 +1,13 @@ +import pytest + +from queries.nbcsports import player_news + + +@pytest.mark.parametrize("page_number", [(1), (2)]) +def test_get_nfl_player_news(page_number: int): + _ = player_news.get_nfl_player_news(page_number) + + +@pytest.mark.parametrize("hours", [(1), (10)]) +def test_get_player_news_window_hours(hours: int): + _ = player_news.get_player_news_window_hours(hours) diff --git a/tests/contract/test_nfl_teams_practice_reports.py b/tests/contract/test_nfl_teams_practice_reports.py new file mode 100644 index 0000000000000000000000000000000000000000..21cacaae49227ac05b0d34917b5e2ad2d88c2373 --- /dev/null +++ b/tests/contract/test_nfl_teams_practice_reports.py @@ -0,0 +1,9 @@ +import pytest + +from domain import teams +from queries.nfl_teams import practice_reports + + +@pytest.mark.parametrize("team", [(x) for x in teams.ALL_TEAMS]) +def test_scrape_team_injury_report(team): + _ = practice_reports.scrape_team_injury_report(team) diff --git a/tests/mocks/2023_keepers.csv b/tests/mocks/2023_keepers.csv new file mode 100644 index 0000000000000000000000000000000000000000..259fd16319db0eebe597900cdbe2ae54225b1b5a --- /dev/null +++ b/tests/mocks/2023_keepers.csv @@ -0,0 +1,222 @@ +,player_id,name,is_keeper,headshot_url,position_type,eligible_positions,selected_position,team_key,team_name,manager,status,season,pick,round,team_key_draft,years_eligible,keeper_cost,eligible +0,25812,Kirk Cousins,False,https://s.yimg.com/iu/api/res/1.2/1ru7GVFJz3q2wjSqH_klXA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/25812.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,52.0,5.0,414.l.474186.t.6,2,4.0,True +1,32692,Justin Jefferson,True,https://s.yimg.com/iu/api/res/1.2/uKAl5.v0hNtF.Gofx7AY5w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/32692.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,148.0,13.0,414.l.474186.t.6,0,12.0,False +2,29399,Tyreek Hill,False,https://s.yimg.com/iu/api/res/1.2/IziJ.2wiZw0IpFOiD2kLsg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/29399.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,21.0,2.0,414.l.474186.t.6,2,1.0,True +3,30120,Mike Williams,True,https://s.yimg.com/iu/api/res/1.2/i9Vi4Ipm6kqfxD38LGUQeA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/30120.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,76.0,7.0,414.l.474186.t.6,1,6.0,True +4,30423,Austin Ekeler,False,https://s.yimg.com/iu/api/res/1.2/Wsn35gKIUHOaQEseGWkfRw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/30423.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,4.0,1.0,414.l.474186.t.6,2,0.0,False +5,31906,Devin Singletary,False,https://s.yimg.com/iu/api/res/1.2/NBqz6HmEaIx19gOlB39KPw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08152023/31906.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,100.0,9.0,414.l.474186.t.6,2,8.0,True +6,30259,George Kittle,False,https://s.yimg.com/iu/api/res/1.2/OaU1XRgACHFYep3esYgmSw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/30259.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,45.0,4.0,414.l.474186.t.6,2,3.0,True +7,33514,Chuba Hubbard,False,https://s.yimg.com/iu/api/res/1.2/Dbzue7q8.gz13D7Jx.oddQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08232022/33514.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,193.0,17.0,414.l.474186.t.9,2,10.0,True +8,32010,Gardner Minshew,False,https://s.yimg.com/iu/api/res/1.2/W5BQWsS0IkvLsIXQTVziuQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/32010.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,,,,2,10.0,True +9,34007,George Pickens,False,https://s.yimg.com/iu/api/res/1.2/2.Q7wW_ectu5_akSvvUDvA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/34007.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,124.0,11.0,414.l.474186.t.6,2,10.0,True +10,32723,Jalen Hurts,True,https://s.yimg.com/iu/api/res/1.2/ZdHzqdvnato.MAwHy9jiWQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/32723.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,93.0,8.0,414.l.474186.t.6,0,7.0,False +11,33971,Jahan Dotson,False,https://s.yimg.com/iu/api/res/1.2/rY7UBr8dJhyBD2zKeDnkAQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/33971.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,172.0,15.0,414.l.474186.t.6,2,14.0,True +12,32719,Chase Claypool,False,https://s.yimg.com/iu/api/res/1.2/GDmycY7ed0Qgt7AIyKjswg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08082023/32719.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,117.0,10.0,414.l.474186.t.6,2,9.0,True +13,33408,Kadarius Toney,False,https://s.yimg.com/iu/api/res/1.2/YgPRacRoiUOxNMcGvJ1qnw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08152023/33408.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,130.0,11.0,414.l.474186.t.7,2,10.0,True +14,31928,Dawson Knox,False,https://s.yimg.com/iu/api/res/1.2/WJptQ610wbYZVND2GLJ1iQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08162022/31928.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,89.0,8.0,414.l.474186.t.1,2,7.0,True +15,27540,Odell Beckham Jr.,False,https://s.yimg.com/iu/api/res/1.2/oIT1EIDlFGEwA81VkFcX7A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/02162023/27540.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,,,,2,10.0,True +16,31137,Daniel Carlson,False,https://s.yimg.com/iu/api/res/1.2/60tYJJYqBaWMBgRufxtW2A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/31137.png,K,['K'],K,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,196.0,17.0,414.l.474186.t.6,2,16.0,True +17,100017,New England,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/nwe.gif,DT,['DEF'],DEF,414.l.474186.t.6,Just Enough Cooks,Gabe,,2022,208.0,18.0,414.l.474186.t.11,2,10.0,True +18,32671,Joe Burrow,False,https://s.yimg.com/iu/api/res/1.2/QJXYvZ8pWkDD6JymTnrKBw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08242022/32671.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,15.0,2.0,414.l.474186.t.7,2,1.0,True +19,27581,Davante Adams,False,https://s.yimg.com/iu/api/res/1.2/UTwCcrglF9.BGBwncQo1Qg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08142023/27581.1.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,10.0,1.0,414.l.474186.t.7,2,0.0,False +20,31883,A.J. Brown,True,https://s.yimg.com/iu/api/res/1.2/n1WlNUDEwJAfqfgp0shFNw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/31883.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,63.0,6.0,414.l.474186.t.7,0,5.0,False +21,31268,Allen Lazard,False,https://s.yimg.com/iu/api/res/1.2/MHhzpDypkk5FcSkgaIwydw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08022023/31268.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,82.0,7.0,414.l.474186.t.7,2,6.0,True +22,30117,Leonard Fournette,False,https://s.yimg.com/iu/api/res/1.2/YKNFujBNRe_snft4cpYzyA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/30117.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.7,Short Stafford,Adam Bush,NA,2022,34.0,3.0,414.l.474186.t.7,2,2.0,True +23,34357,Zonovan Knight,False,https://s.yimg.com/iu/api/res/1.2/th5obr_yU_FnvBc.oRysIQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/34357.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,,,,2,10.0,True +24,31019,Dallas Goedert,True,https://s.yimg.com/iu/api/res/1.2/2EigEb4xahJnP3QtYDZg2w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/31019.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,106.0,9.0,414.l.474186.t.7,1,8.0,True +25,33996,Kenneth Walker III,False,https://s.yimg.com/iu/api/res/1.2/P3hgQ_501q_FSeopp00fXQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07252023/33996.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,111.0,10.0,414.l.474186.t.7,2,9.0,True +26,34030,Desmond Ridder,False,https://s.yimg.com/iu/api/res/1.2/DUrt_8tP5WTT_gHETdjCzg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08192022/34030.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,188.0,16.0,414.l.474186.t.3,2,10.0,True +27,24060,Colt McCoy,False,https://s.yimg.com/iu/api/res/1.2/ad_RXu.DQLICW71zcK05SQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/24060.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.7,Short Stafford,Adam Bush,Q,2022,,,,2,10.0,True +28,33989,Christian Watson,False,https://s.yimg.com/iu/api/res/1.2/6IIpLPG5eq.B8RZTCtwmhQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08302022/33989.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,183.0,16.0,414.l.474186.t.7,2,15.0,True +29,29360,Demarcus Robinson,False,https://s.yimg.com/iu/api/res/1.2/M2p6MNVcinIE1ybyAMI5yw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/11282022/29360.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.7,Short Stafford,Adam Bush,Q,2022,,,,2,10.0,True +30,33443,Pat Freiermuth,False,https://s.yimg.com/iu/api/res/1.2/8K1q_4oL5zFCQOAUbcU_TQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/33443.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.7,Short Stafford,Adam Bush,Q,2022,160.0,14.0,414.l.474186.t.11,2,10.0,True +31,30153,Curtis Samuel,False,https://s.yimg.com/iu/api/res/1.2/3RkjwLTDVeSKnGZAIaPL0Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/30153.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,,,,2,10.0,True +32,32846,K.J. Osborn,False,https://s.yimg.com/iu/api/res/1.2/FHiM_yRbyAmJAkQwfopEDQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/32846.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,215.0,18.0,414.l.474186.t.10,2,10.0,True +33,30426,Younghoe Koo,False,https://s.yimg.com/iu/api/res/1.2/5SBc_xWFvSQDka.GZA77pw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08192022/30426.png,K,['K'],K,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,201.0,17.0,414.l.474186.t.11,2,10.0,True +34,100021,Philadelphia,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/phi.gif,DT,['DEF'],DEF,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,202.0,17.0,414.l.474186.t.7,2,16.0,True +35,100019,New York,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/nyg_2.gif,DT,['DEF'],BN,414.l.474186.t.7,Short Stafford,Adam Bush,,2022,,,,2,10.0,True +36,33991,Breece Hall,False,https://s.yimg.com/iu/api/res/1.2/fknRjJHFW3aHaUGO8ssHZg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/33991.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",IR,414.l.474186.t.7,Short Stafford,Adam Bush,Q,2022,39.0,4.0,414.l.474186.t.7,2,3.0,True +37,30123,Patrick Mahomes,False,https://s.yimg.com/iu/api/res/1.2/uGaZr6pMT6iteP6VNUutXA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/30123.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.1,Solow Red Cups,Jon,Q,2022,8.0,1.0,414.l.474186.t.1,2,0.0,False +38,31896,DK Metcalf,False,https://s.yimg.com/iu/api/res/1.2/fQ3vz6SwKcRb0sOGr6xjUA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07252023/31896.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.1,Solow Red Cups,Jon,,2022,41.0,4.0,414.l.474186.t.1,2,3.0,True +39,33500,Amon-Ra St. Brown,True,https://s.yimg.com/iu/api/res/1.2/znSCRKLz1GvciatCpJ40Hw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08302022/33500.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.1,Solow Red Cups,Jon,,2022,200.0,17.0,414.l.474186.t.1,1,16.0,True +40,32523,Greg Dortch,False,https://s.yimg.com/iu/api/res/1.2/1n5zauQsXxaPrKLMTsOFlw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/32523.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.1,Solow Red Cups,Jon,,2022,,,,2,10.0,True +41,30972,Saquon Barkley,False,https://s.yimg.com/iu/api/res/1.2/mfHTfkJ107RvBncwqjnr9A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09102022/30972.1.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.1,Solow Red Cups,Jon,,2022,17.0,2.0,414.l.474186.t.1,2,1.0,True +42,33508,Rhamondre Stevenson,False,https://s.yimg.com/iu/api/res/1.2/y3pOEJ5ze52.TwQwLfzJUw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/33508.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.1,Solow Red Cups,Jon,,2022,56.0,5.0,414.l.474186.t.1,2,4.0,True +43,31127,Tyler Conklin,False,https://s.yimg.com/iu/api/res/1.2/4melcS_WZ1rv3aEFcKLvkw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/31127.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.1,Solow Red Cups,Jon,,2022,,,,2,10.0,True +44,33413,Travis Etienne Jr.,True,https://s.yimg.com/iu/api/res/1.2/D1RbhndjK1UiNP_iBg119g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/33413.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.1,Solow Red Cups,Jon,,2022,113.0,10.0,414.l.474186.t.1,1,9.0,True +45,30977,Josh Allen,True,https://s.yimg.com/iu/api/res/1.2/2QJbc1u1X6VJqFGDiBcSHw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08162022/30977.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.1,Solow Red Cups,Jon,,2022,32.0,3.0,414.l.474186.t.1,0,2.0,False +46,31857,Marquise Brown,False,https://s.yimg.com/iu/api/res/1.2/.CMTa_qhHj5x9VgqxEceSg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08222022/31857.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.1,Solow Red Cups,Jon,,2022,65.0,6.0,414.l.474186.t.1,2,5.0,True +47,34047,Rachaad White,False,https://s.yimg.com/iu/api/res/1.2/WwOHaEZZDPASCr3MR7iLEw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08182023/34047.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.1,Solow Red Cups,Jon,,2022,128.0,11.0,414.l.474186.t.1,2,10.0,True +48,33975,Kenny Pickett,False,https://s.yimg.com/iu/api/res/1.2/4FNm5KnyTOOqgi8IPlu2yg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/33975.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.1,Solow Red Cups,Jon,,2022,152.0,13.0,414.l.474186.t.1,2,12.0,True +49,30199,Kareem Hunt,False,https://s.yimg.com/iu/api/res/1.2/qImyalciOvrIvq611mTuxQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09132022/30199.1.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.1,Solow Red Cups,Jon,NA,2022,72.0,6.0,414.l.474186.t.1,2,5.0,True +50,33538,Kenneth Gainwell,False,https://s.yimg.com/iu/api/res/1.2/Qa8DZYmDuoB9.VvFRQ6bBA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/33538.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.1,Solow Red Cups,Jon,,2022,137.0,12.0,414.l.474186.t.1,2,11.0,True +51,32877,Isaiah Hodgins,False,https://s.yimg.com/iu/api/res/1.2/_20CSLAepNKXOc.ELTKcpg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/11142022/32877.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.1,Solow Red Cups,Jon,,2022,,,,2,10.0,True +52,34079,Isaiah Spiller,False,https://s.yimg.com/iu/api/res/1.2/UtIYVmtNsvlANKEF2kwS.A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/34079.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.1,Solow Red Cups,Jon,,2022,209.0,18.0,414.l.474186.t.1,2,17.0,True +53,9526,Graham Gano,False,https://s.yimg.com/iu/api/res/1.2/7_9bZ4Hysr0w4YsNg8rUOg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09102022/9526.1.png,K,['K'],K,414.l.474186.t.1,Solow Red Cups,Jon,,2022,,,,2,10.0,True +54,100012,Kansas City,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/kan.gif,DT,['DEF'],DEF,414.l.474186.t.1,Solow Red Cups,Jon,,2022,,,,2,10.0,True +55,33998,Wan'Dale Robinson,False,https://s.yimg.com/iu/api/res/1.2/0Z.sjWpUZoE4iq.6_wPWzA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/33998.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",IR,414.l.474186.t.1,Solow Red Cups,Jon,Q,2022,161.0,14.0,414.l.474186.t.1,2,13.0,True +56,33399,Justin Fields,True,https://s.yimg.com/iu/api/res/1.2/bBjs6pBZ9oDYeFwr4OeCiA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08032022/33399.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.2,Gold Standard,Daniel,,2022,86.0,8.0,414.l.474186.t.2,1,7.0,True +57,28534,Stefon Diggs,True,https://s.yimg.com/iu/api/res/1.2/80tJkiyYc0vrrvUjfh2auw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/10012020/28534.1.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.2,Gold Standard,Daniel,,2022,35.0,3.0,414.l.474186.t.2,0,2.0,False +58,33398,DeVonta Smith,False,https://s.yimg.com/iu/api/res/1.2/6hdcVBZqvse4ZSmDUfFEdQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/33398.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.2,Gold Standard,Daniel,,2022,83.0,7.0,414.l.474186.t.2,2,6.0,True +59,33965,Garrett Wilson,False,https://s.yimg.com/iu/api/res/1.2/VSzU4ioKKF77t7yr5uQLNg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/33965.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.2,Gold Standard,Daniel,,2022,206.0,18.0,414.l.474186.t.2,2,17.0,True +60,32732,AJ Dillon,True,https://s.yimg.com/iu/api/res/1.2/Fuzt08n6yShzPhmZk3dGPQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08302022/32732.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.2,Gold Standard,Daniel,,2022,62.0,6.0,414.l.474186.t.2,1,5.0,True +61,30202,D'Onta Foreman,False,https://s.yimg.com/iu/api/res/1.2/xwIHvz1F6dEQPoWawE5Vrg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08082023/30202.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.2,Gold Standard,Daniel,,2022,198.0,17.0,414.l.474186.t.5,2,10.0,True +62,30136,Evan Engram,False,https://s.yimg.com/iu/api/res/1.2/o6Ww.Q9ytv48B9LNAHMv8A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/30136.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.2,Gold Standard,Daniel,,2022,,,,2,10.0,True +63,34207,Isiah Pacheco,False,https://s.yimg.com/iu/api/res/1.2/MLVZ02I_Cn_qG5u2gcG3iw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/34207.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.2,Gold Standard,Daniel,Q,2022,134.0,12.0,414.l.474186.t.2,2,11.0,True +64,27560,Teddy Bridgewater,False,https://s.yimg.com/iu/api/res/1.2/V4npHYJxJlm0HzNwXMTnag--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/27560.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.2,Gold Standard,Daniel,,2022,,,,2,10.0,True +65,32756,Zack Moss,False,https://s.yimg.com/iu/api/res/1.2/WxwF7udgeEEeuQ0Wdmo85w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/32756.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.2,Gold Standard,Daniel,,2022,,,,2,10.0,True +66,30295,Aaron Jones,False,https://s.yimg.com/iu/api/res/1.2/ez8dGQIh4snWNJ.0GsSe3Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08302022/30295.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.2,Gold Standard,Daniel,,2022,14.0,2.0,414.l.474186.t.2,2,1.0,True +67,26804,Latavius Murray,False,https://s.yimg.com/iu/api/res/1.2/QbmUSNHnwM45rtIqakYvfQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08012023/26804.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.2,Gold Standard,Daniel,,2022,,,,2,10.0,True +68,31107,Dalton Schultz,False,https://s.yimg.com/iu/api/res/1.2/UKeiRn.vKvlPKMjjXmQAwQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08152023/31107.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.2,Gold Standard,Daniel,,2022,59.0,5.0,414.l.474186.t.2,2,4.0,True +69,30285,Isaiah McKenzie,False,https://s.yimg.com/iu/api/res/1.2/ElcW35CLgX7r2P_CB_7yJw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08162022/30285.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.2,Gold Standard,Daniel,,2022,110.0,10.0,414.l.474186.t.2,2,9.0,True +70,30266,Jake Elliott,False,https://s.yimg.com/iu/api/res/1.2/cJxU0FO_kfryoXKrAKY9iA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/30266.png,K,['K'],K,414.l.474186.t.2,Gold Standard,Daniel,,2022,,,,2,10.0,True +71,100028,Washington,False,https://s.yimg.com/cv/apiv2/default/nfl/20200909/50x50/cr/washington.gif,DT,['DEF'],DEF,414.l.474186.t.2,Gold Standard,Daniel,,2022,,,,2,10.0,True +72,100002,Buffalo,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/buf.gif,DT,['DEF'],BN,414.l.474186.t.2,Gold Standard,Daniel,,2022,107.0,9.0,414.l.474186.t.2,2,8.0,True +73,32675,Tua Tagovailoa,True,https://s.yimg.com/iu/api/res/1.2/S0TI9tF0kmgmb5qpOEbV8w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/32675.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.10,The Jenbots,Jen,,2022,98.0,9.0,414.l.474186.t.10,1,8.0,True +74,27277,Adam Thielen,False,https://s.yimg.com/iu/api/res/1.2/eB_EQ1lpxyq3QGcTGyk1mQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07272023/27277.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.10,The Jenbots,Jen,,2022,74.0,7.0,414.l.474186.t.10,2,6.0,True +75,33394,Jaylen Waddle,True,https://s.yimg.com/iu/api/res/1.2/H7eEPYlww.bcZ9r9gY8fPg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/33394.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.10,The Jenbots,Jen,,2022,122.0,11.0,414.l.474186.t.10,1,10.0,True +76,30994,DJ Moore,False,https://s.yimg.com/iu/api/res/1.2/3K7Sh.Q0fLpdyh7g2QD.nA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08082023/30994.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.10,The Jenbots,Jen,,2022,47.0,4.0,414.l.474186.t.10,2,3.0,True +77,31005,Nick Chubb,False,https://s.yimg.com/iu/api/res/1.2/btOicZxjBcHhRuOC9nIV1g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09132022/31005.1.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.10,The Jenbots,Jen,,2022,23.0,2.0,414.l.474186.t.10,2,1.0,True +78,29238,Ezekiel Elliott,False,https://s.yimg.com/iu/api/res/1.2/DUODUN2PLd2SHxwOt85rLg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08092022/29238.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.10,The Jenbots,Jen,,2022,26.0,3.0,414.l.474186.t.10,2,2.0,True +79,34036,Greg Dulcich,False,https://s.yimg.com/iu/api/res/1.2/BpDTU3QlurqLMDIerAVnnQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08262022/34036.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.10,The Jenbots,Jen,Q,2022,,,,2,10.0,True +80,31905,David Montgomery,False,https://s.yimg.com/iu/api/res/1.2/wRdrI7HtYiZfB8U243TfhQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08112023/31905.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.10,The Jenbots,Jen,,2022,50.0,5.0,414.l.474186.t.10,2,4.0,True +81,29235,Jared Goff,False,https://s.yimg.com/iu/api/res/1.2/Ho6Q1LTikrPbhrhWo2bwCQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08302022/29235.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.10,The Jenbots,Jen,,2022,71.0,6.0,414.l.474186.t.10,2,5.0,True +82,29341,Chris Moore,False,https://s.yimg.com/iu/api/res/1.2/UDZSy6kjfiMqc.GHwYV9WQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/29341.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.10,The Jenbots,Jen,,2022,,,,2,10.0,True +83,30247,Jamaal Williams,False,https://s.yimg.com/iu/api/res/1.2/NIDjtpLVqfsxwMAUB.yA9Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08082023/30247.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.10,The Jenbots,Jen,,2022,143.0,12.0,414.l.474186.t.10,2,11.0,True +84,30995,Hayden Hurst,False,https://s.yimg.com/iu/api/res/1.2/Rdja2nigOEqsGZjx6nNDtw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07272023/30995.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.10,The Jenbots,Jen,,2022,,,,2,10.0,True +85,24822,Andy Dalton,False,https://s.yimg.com/iu/api/res/1.2/Ohoq1e_3muN0RKhB63.mVw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07272023/24822.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.10,The Jenbots,Jen,,2022,,,,2,10.0,True +86,34008,Alec Pierce,False,https://s.yimg.com/iu/api/res/1.2/8OKDszvwdUD9QqJoN5h6DA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09012022/34008.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.10,The Jenbots,Jen,,2022,,,,2,10.0,True +87,33477,Nico Collins,False,https://s.yimg.com/iu/api/res/1.2/e6yVOlCA1IHCbRbTinF_5A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/33477.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.10,The Jenbots,Jen,Q,2022,146.0,13.0,414.l.474186.t.10,2,12.0,True +88,33973,Treylon Burks,False,https://s.yimg.com/iu/api/res/1.2/HY6f8mRVR6_LrFEFNrwfFA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08182023/33973.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.10,The Jenbots,Jen,,2022,136.0,12.0,414.l.474186.t.11,2,10.0,True +89,34344,Cameron Dicker,False,https://s.yimg.com/iu/api/res/1.2/XAI1iHGCrUs2Y71Z4dZOAA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08112023/34344.png,K,['K'],K,414.l.474186.t.10,The Jenbots,Jen,,2022,,,,2,10.0,True +90,100007,Denver,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/den.gif,DT,['DEF'],DEF,414.l.474186.t.10,The Jenbots,Jen,,2022,170.0,15.0,414.l.474186.t.10,2,14.0,True +91,33637,Ben Skowronek,False,https://s.yimg.com/iu/api/res/1.2/uLa2iCk.CU0BqrQxRABn7g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/33637.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",IR,414.l.474186.t.10,The Jenbots,Jen,Q,2022,,,,2,10.0,True +92,29369,Dak Prescott,False,https://s.yimg.com/iu/api/res/1.2/KE1Rw3r7nA8anql98tZI4Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08092022/29369.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,20.0,2.0,414.l.474186.t.3,2,1.0,True +93,30197,Chris Godwin,False,https://s.yimg.com/iu/api/res/1.2/roNVzlSNZyCVTP_YD2gH.Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08182023/30197.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,68.0,6.0,414.l.474186.t.3,2,5.0,True +94,32798,Gabe Davis,False,https://s.yimg.com/iu/api/res/1.2/FpnVozvf7BEniT.DSR1bbg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08162022/32798.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,29.0,3.0,414.l.474186.t.3,2,2.0,True +95,34659,Rashid Shaheed,False,https://s.yimg.com/iu/api/res/1.2/Yv.lEisn6gZ.6d9gJUFRhg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09112022/34659.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,,,,2,10.0,True +96,30154,Dalvin Cook,False,https://s.yimg.com/iu/api/res/1.2/PEteLdlAzix1arTWEMNdqQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/30154.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.3,Donā€™t Fear the Keeper,David,Q,2022,5.0,1.0,414.l.474186.t.3,2,0.0,False +97,26652,Cordarrelle Patterson,True,https://s.yimg.com/iu/api/res/1.2/iGQ1kha0acy.9HNIc2OXiw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08192022/26652.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,101.0,9.0,414.l.474186.t.3,1,8.0,True +98,29269,Hunter Henry,False,https://s.yimg.com/iu/api/res/1.2/NEm1DAWA.yuFGnX1llY2CA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/29269.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,167.0,14.0,414.l.474186.t.10,2,10.0,True +99,31051,Michael Gallup,True,https://s.yimg.com/iu/api/res/1.2/rc2qooGsh7s6eFSb2i4UIg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08092022/31051.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.3,Donā€™t Fear the Keeper,David,Q,2022,116.0,10.0,414.l.474186.t.3,1,9.0,True +100,33389,Trevor Lawrence,False,https://s.yimg.com/iu/api/res/1.2/dTvmt7jY2XE8d0K6jMnytQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/33389.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,44.0,4.0,414.l.474186.t.3,2,3.0,True +101,32736,Antonio Gibson,False,https://s.yimg.com/iu/api/res/1.2/6ofK.flGUG.stOTsHIfLjQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/32736.1.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.3,Donā€™t Fear the Keeper,David,Q,2022,53.0,5.0,414.l.474186.t.3,2,4.0,True +102,32003,Darius Slayton,False,https://s.yimg.com/iu/api/res/1.2/E8si81KuaPrD2lFQ0GtLbw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09102022/32003.1.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,,,,2,10.0,True +103,28839,Taylor Heinicke,False,https://s.yimg.com/iu/api/res/1.2/89aG76v4MRKpCk2hfNizJw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07272023/28839.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,,,,2,10.0,True +104,31934,Alexander Mattison,False,https://s.yimg.com/iu/api/res/1.2/4Td_gb.cVkIChxsrHAqvjA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/31934.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,125.0,11.0,414.l.474186.t.3,2,10.0,True +105,33113,Juwan Johnson,False,https://s.yimg.com/iu/api/res/1.2/_eJHA9kCwXPo6vNRfEt2sQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09112022/33113.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,,,,2,10.0,True +106,30973,Sam Darnold,False,https://s.yimg.com/iu/api/res/1.2/IIsm16mUSjOkQICx7Duc8w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07262023/30973.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,,,,2,10.0,True +107,29236,Carson Wentz,False,https://s.yimg.com/iu/api/res/1.2/CZ4byGFTeTX_PjRU9.kRuQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/29236.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.3,Donā€™t Fear the Keeper,David,NA,2022,149.0,13.0,414.l.474186.t.3,2,12.0,True +108,8432,Nick Folk,False,https://s.yimg.com/iu/api/res/1.2/F0y5frwLiZA3eW7iKP_jMw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/8432.png,K,['K'],K,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,,,,2,10.0,True +109,100027,Tampa Bay,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/tam.gif,DT,['DEF'],DEF,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,140.0,12.0,414.l.474186.t.3,2,11.0,True +110,33437,Rondale Moore,False,https://s.yimg.com/iu/api/res/1.2/yx6mu2I6XLuhhF0bScblZA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/33437.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",IR,414.l.474186.t.3,Donā€™t Fear the Keeper,David,,2022,164.0,14.0,414.l.474186.t.3,2,13.0,True +111,7200,Aaron Rodgers,False,https://s.yimg.com/iu/api/res/1.2/wBl5RxV59_1mMQkQHhlz_A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08022023/7200.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.11,Fired on my day off,Cobi,,2022,33.0,3.0,414.l.474186.t.11,2,2.0,True +112,32687,CeeDee Lamb,True,https://s.yimg.com/iu/api/res/1.2/AaMdFg_1O0IbpNi3gFRxGg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08092022/32687.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.11,Fired on my day off,Cobi,,2022,81.0,7.0,414.l.474186.t.11,0,6.0,False +113,32695,Brandon Aiyuk,True,https://s.yimg.com/iu/api/res/1.2/mLlExLggroKYPjOXnGp50A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/32695.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.11,Fired on my day off,Cobi,,2022,88.0,8.0,414.l.474186.t.11,0,7.0,False +114,29288,Tyler Boyd,False,https://s.yimg.com/iu/api/res/1.2/8eGzZ4Z.xXR0iPQotLXLRQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08242022/29288.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.11,Fired on my day off,Cobi,Q,2022,129.0,11.0,414.l.474186.t.11,2,10.0,True +115,30218,James Conner,True,https://s.yimg.com/iu/api/res/1.2/3XWaWcJhci_WmQzjkaJk2w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/30218.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.11,Fired on my day off,Cobi,,2022,64.0,6.0,414.l.474186.t.11,1,5.0,True +116,32722,Cam Akers,True,https://s.yimg.com/iu/api/res/1.2/yt8Azi2B44fcaSIY45M3WQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/32722.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.11,Fired on my day off,Cobi,,2022,118.0,10.0,414.l.474186.t.8,1,9.0,True +117,32713,Cole Kmet,False,https://s.yimg.com/iu/api/res/1.2/5OVkUh_qhJlTyp6RY6xXXQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08032022/32713.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.11,Fired on my day off,Cobi,,2022,95.0,8.0,414.l.474186.t.10,2,7.0,True +118,33412,Najee Harris,False,https://s.yimg.com/iu/api/res/1.2/OR95VsfQ4o4K6THKBgp5yA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/33412.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.11,Fired on my day off,Cobi,,2022,9.0,1.0,414.l.474186.t.11,2,0.0,False +119,25785,Russell Wilson,False,https://s.yimg.com/iu/api/res/1.2/FAeFxNvgxIInhiyZhYp6FQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08262022/25785.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.11,Fired on my day off,Cobi,Q,2022,16.0,2.0,414.l.474186.t.11,2,1.0,True +120,34107,Tyler Allgeier,False,https://s.yimg.com/iu/api/res/1.2/5tRMs86CGOTiPIS1sSl.qw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08192022/34107.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.11,Fired on my day off,Cobi,,2022,171.0,15.0,414.l.474186.t.8,2,10.0,True +121,31852,Noah Fant,False,https://s.yimg.com/iu/api/res/1.2/DjDtCvS.YcRZ1AIzoA8OAw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/31852.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.11,Fired on my day off,Cobi,,2022,,,,2,10.0,True +122,28654,Raheem Mostert,False,https://s.yimg.com/iu/api/res/1.2/5B8rSiAkQu_C.T1FzwOuYQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/10132022/28654.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.11,Fired on my day off,Cobi,,2022,112.0,10.0,414.l.474186.t.11,2,9.0,True +123,33422,Elijah Moore,True,https://s.yimg.com/iu/api/res/1.2/Vkh0utH3qjiaFHtrNHSQRQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08022023/33422.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.11,Fired on my day off,Cobi,,2022,114.0,10.0,414.l.474186.t.4,1,9.0,True +124,30182,Cooper Kupp,False,https://s.yimg.com/iu/api/res/1.2/r4cot0junNdG6BI.2PKXDA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/30182.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.11,Fired on my day off,Cobi,Q,2022,2.0,1.0,414.l.474186.t.10,2,0.0,False +125,33391,Trey Lance,True,https://s.yimg.com/iu/api/res/1.2/H0GY3TXYTlkKJdLioJmvig--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/33391.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.11,Fired on my day off,Cobi,,2022,91.0,8.0,414.l.474186.t.5,1,7.0,True +126,31977,Matt Gay,False,https://s.yimg.com/iu/api/res/1.2/lHuyp4cfi0SGZO3XhtX5PA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/31977.png,K,['K'],K,414.l.474186.t.11,Fired on my day off,Cobi,,2022,182.0,16.0,414.l.474186.t.2,2,10.0,True +127,100030,Jacksonville,False,https://s.yimg.com/xe/ipt/50x50w.3.gif,DT,['DEF'],DEF,414.l.474186.t.11,Fired on my day off,Cobi,,2022,,,,2,10.0,True +128,100024,Los Angeles,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/sdg3.jpg,DT,['DEF'],BN,414.l.474186.t.11,Fired on my day off,Cobi,,2022,211.0,18.0,414.l.474186.t.5,2,10.0,True +129,31833,Kyler Murray,False,https://s.yimg.com/iu/api/res/1.2/.c7dDweSvYsX9He5BCXNBw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/31833.png,O,"['QB', 'Q/W/R/T']",IR,414.l.474186.t.11,Fired on my day off,Cobi,Q,2022,11.0,1.0,414.l.474186.t.2,2,0.0,False +130,34218,Brock Purdy,False,https://s.yimg.com/iu/api/res/1.2/g0DerZ8KoS3DfFhcmgN6eA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/34218.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.5,Only Bad Juju Here,Will,Q,2022,,,,2,10.0,True +131,30175,JuJu Smith-Schuster,False,https://s.yimg.com/iu/api/res/1.2/VkzzBWy2DIUaQe_tV7U4vw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08082023/30175.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,54.0,5.0,414.l.474186.t.5,2,4.0,True +132,33393,Ja'Marr Chase,True,https://s.yimg.com/iu/api/res/1.2/BW26kQyt4fyE_Jh6VoLb_g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08242022/33393.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,43.0,4.0,414.l.474186.t.5,1,3.0,True +133,32704,Michael Pittman Jr.,True,https://s.yimg.com/iu/api/res/1.2/NraGNpZU47CL52KvePBgBQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09012022/32704.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,115.0,10.0,414.l.474186.t.5,1,9.0,True +134,27624,Jerick McKinnon,False,https://s.yimg.com/iu/api/res/1.2/v.6YTAtt5Bx_r8IsbuF4Gw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/27624.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,,,,2,10.0,True +135,30161,Joe Mixon,False,https://s.yimg.com/iu/api/res/1.2/UmC_omZu1fTvyauPzw.TiA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08242022/30161.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,6.0,1.0,414.l.474186.t.5,2,0.0,False +136,26686,Travis Kelce,False,https://s.yimg.com/iu/api/res/1.2/ROLrLEfO0ysGN.bsgSza3Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/26686.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,19.0,2.0,414.l.474186.t.5,2,1.0,True +137,32725,J.K. Dobbins,True,https://s.yimg.com/iu/api/res/1.2/TM_L8PnmPlQ5lh_cDwmqNw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08222022/32725.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,99.0,9.0,414.l.474186.t.8,1,8.0,True +138,32685,Jerry Jeudy,False,https://s.yimg.com/iu/api/res/1.2/bPjROeGlioyNPk0xHBu7_w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08262022/32685.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,78.0,7.0,414.l.474186.t.5,2,6.0,True +139,33966,Chris Olave,False,https://s.yimg.com/iu/api/res/1.2/YZPLrSB.eOYS8Q5zM_uLrg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09112022/33966.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,102.0,9.0,414.l.474186.t.5,2,8.0,True +140,32231,Jakobi Meyers,False,https://s.yimg.com/iu/api/res/1.2/9wyBwt_A4z1ltXnpNbKD5g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/32231.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,174.0,15.0,414.l.474186.t.5,2,14.0,True +141,30150,Zay Jones,False,https://s.yimg.com/iu/api/res/1.2/I3r.AujpA.yEcNZPQr26jw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/30150.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,,,,2,10.0,True +142,31803,John Wolford,False,https://s.yimg.com/iu/api/res/1.2/Tv_XW9BxUjJ4.U8FJN3IGQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07272023/31803.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,,,,2,10.0,True +143,30614,Taysom Hill,False,https://s.yimg.com/iu/api/res/1.2/p.1N.FtMvV4GHcMxHdjUww--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09112022/30614.png,O,"['QB', 'TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,,,,2,10.0,True +144,33455,Davis Mills,False,https://s.yimg.com/iu/api/res/1.2/DDzxF1vCU7TQ6cMzzRhyhA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/33455.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,158.0,14.0,414.l.474186.t.2,2,10.0,True +145,31074,Nyheim Hines,False,https://s.yimg.com/iu/api/res/1.2/TIZ15raJKQHYY1QNCMTPSg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08012023/31074.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.5,Only Bad Juju Here,Will,NA,2022,126.0,11.0,414.l.474186.t.5,2,10.0,True +146,33537,Evan McPherson,False,https://s.yimg.com/iu/api/res/1.2/hwv0TonIqav8wiFggZSj6g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08242022/33537.png,K,['K'],K,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,150.0,13.0,414.l.474186.t.5,2,12.0,True +147,100025,San Francisco,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/sfo.gif,DT,['DEF'],DEF,414.l.474186.t.5,Only Bad Juju Here,Will,,2022,139.0,12.0,414.l.474186.t.5,2,11.0,True +148,32993,Tyler Huntley,False,https://s.yimg.com/iu/api/res/1.2/alizLiWws0hc6yNV_gUqEQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08222022/32993.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.12,Watson Watsoff,Ely,,2022,,,,2,10.0,True +149,26699,Keenan Allen,False,https://s.yimg.com/iu/api/res/1.2/gCbEcOZk_Lh8EHBnSx1isQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/26699.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.12,Watson Watsoff,Ely,,2022,36.0,3.0,414.l.474186.t.12,2,2.0,True +150,26664,Robert Woods,False,https://s.yimg.com/iu/api/res/1.2/yqQBSqw646fbFVhB.jGzHQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08152023/26664.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.12,Watson Watsoff,Ely,,2022,84.0,7.0,414.l.474186.t.12,2,6.0,True +151,31981,Hunter Renfrow,False,https://s.yimg.com/iu/api/res/1.2/u8BAIFQqXyupdqQAw_Hm6Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/31981.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.12,Watson Watsoff,Ely,,2022,77.0,7.0,414.l.474186.t.3,2,6.0,True +152,31885,Miles Sanders,False,https://s.yimg.com/iu/api/res/1.2/9lD_ronp9xhQnVee4jjypA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07272023/31885.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.12,Watson Watsoff,Ely,,2022,61.0,6.0,414.l.474186.t.12,2,5.0,True +153,30180,Alvin Kamara,False,https://s.yimg.com/iu/api/res/1.2/viS6OEQEIhOpJ7DMpz6dBw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09112022/30180.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.12,Watson Watsoff,Ely,,2022,13.0,2.0,414.l.474186.t.12,2,1.0,True +154,28592,Darren Waller,False,https://s.yimg.com/iu/api/res/1.2/huxiwRVZLOt1t2B.XWU0zg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08072023/28592.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.12,Watson Watsoff,Ely,,2022,60.0,5.0,414.l.474186.t.12,2,4.0,True +155,34054,Brian Robinson,False,https://s.yimg.com/iu/api/res/1.2/Ryadq.naQHcX5OZnqTXSZQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/34054.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.12,Watson Watsoff,Ely,Q,2022,157.0,14.0,414.l.474186.t.12,2,13.0,True +156,5228,Tom Brady,False,https://s.yimg.com/iu/api/res/1.2/Ol_o6nNm9irLqsJ.2ZxQHg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/5228.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.12,Watson Watsoff,Ely,NA,2022,37.0,4.0,414.l.474186.t.12,2,3.0,True +157,26650,DeAndre Hopkins,False,https://s.yimg.com/iu/api/res/1.2/BSf2ob2XIUFixJRPTv850w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/26650.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.12,Watson Watsoff,Ely,,2022,109.0,10.0,414.l.474186.t.12,2,9.0,True +158,30118,Corey Davis,False,https://s.yimg.com/iu/api/res/1.2/9VJg4F5pJoJ32he4nwbCsw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/30118.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.12,Watson Watsoff,Ely,,2022,,,,2,10.0,True +159,31960,Tony Pollard,False,https://s.yimg.com/iu/api/res/1.2/S63xm6zuI1a5XIIfzYtO3w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08092022/31960.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.12,Watson Watsoff,Ely,Q,2022,85.0,8.0,414.l.474186.t.12,2,7.0,True +160,31002,Lamar Jackson,True,https://s.yimg.com/iu/api/res/1.2/0hA844ZfAfY8qn_CfORdtw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08222022/31002.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.12,Watson Watsoff,Ely,,2022,12.0,1.0,414.l.474186.t.12,1,0.0,False +161,29344,Tyler Higbee,False,https://s.yimg.com/iu/api/res/1.2/H7IhC3F_OhDIHM.6hyR4PQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/29344.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.12,Watson Watsoff,Ely,,2022,,,,2,10.0,True +162,31424,Gus Edwards,False,https://s.yimg.com/iu/api/res/1.2/QRCYxnhZdLNzkmTD37IyYQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08222022/31424.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.12,Watson Watsoff,Ely,,2022,205.0,18.0,414.l.474186.t.12,2,17.0,True +163,26534,Justin Tucker,False,https://s.yimg.com/iu/api/res/1.2/J2_kNRYOP0qqVUjUAmlY5w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08222022/26534.png,K,['K'],K,414.l.474186.t.12,Watson Watsoff,Ely,,2022,132.0,11.0,414.l.474186.t.12,2,10.0,True +164,7520,Robbie Gould,False,https://s.yimg.com/iu/api/res/1.2/2zhwD2efs8sK15WRX0dwjw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/7520.png,K,['K'],BN,414.l.474186.t.12,Watson Watsoff,Ely,NA,2022,,,,2,10.0,True +165,100006,Dallas,False,https://s.yimg.com/cv/apiv2/default/nfl/20200908/50x50/cr/cowboys.png,DT,['DEF'],DEF,414.l.474186.t.12,Watson Watsoff,Ely,,2022,181.0,16.0,414.l.474186.t.12,2,15.0,True +166,27591,Jarvis Landry,False,https://s.yimg.com/iu/api/res/1.2/MRwafrUVqUkGohu_PCZ8.A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09112022/27591.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",IR,414.l.474186.t.12,Watson Watsoff,Ely,NA,2022,144.0,12.0,414.l.474186.t.9,2,10.0,True +167,26662,Geno Smith,False,https://s.yimg.com/iu/api/res/1.2/E9txpiDl4SjiijhfG_larA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07252023/26662.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,162.0,14.0,414.l.474186.t.4,2,13.0,True +168,32703,Tee Higgins,True,https://s.yimg.com/iu/api/res/1.2/eelMCXUfHYBxtuwYpAzObQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08242022/32703.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,31.0,3.0,414.l.474186.t.4,1,2.0,True +169,31210,Richie James,False,https://s.yimg.com/iu/api/res/1.2/G3M0VC0mAmv2kt.xlBAmAQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08152023/31210.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,,,,2,10.0,True +170,31898,Diontae Johnson,True,https://s.yimg.com/iu/api/res/1.2/rWYS3gd7L1OSmCvaDD5O_g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/31898.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,42.0,4.0,414.l.474186.t.4,1,3.0,True +171,29279,Derrick Henry,False,https://s.yimg.com/iu/api/res/1.2/axybdmTm92jaZUw5ZMNwog--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08182023/29279.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,7.0,1.0,414.l.474186.t.4,2,0.0,False +172,32705,D'Andre Swift,False,https://s.yimg.com/iu/api/res/1.2/MKPJLDRXqjHsT2XLdZ0aJg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08042023/32705.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,18.0,2.0,414.l.474186.t.4,2,1.0,True +173,31840,T.J. Hockenson,False,https://s.yimg.com/iu/api/res/1.2/gDcUMhQ1yyyobQf7E8QcxA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08092023/31840.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,90.0,8.0,414.l.474186.t.4,2,7.0,True +174,33605,Khalil Herbert,False,https://s.yimg.com/iu/api/res/1.2/qdOtFpdIP3YkVtjGnwskIg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08032022/33605.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,127.0,11.0,414.l.474186.t.4,2,10.0,True +175,31838,Daniel Jones,False,https://s.yimg.com/iu/api/res/1.2/kZt6H9OCdJ_0aE3Bq3eC7g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09102022/31838.1.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,55.0,5.0,414.l.474186.t.4,2,4.0,True +176,33465,Joshua Palmer,False,https://s.yimg.com/iu/api/res/1.2/JAFW6yNN.sTLPmKLZ8T.0Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/33465.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,186.0,16.0,414.l.474186.t.4,2,15.0,True +177,28457,Tyler Lockett,False,https://s.yimg.com/iu/api/res/1.2/NYan.iJFfM0a3h1aJ_Qb.w--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07252023/28457.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,103.0,9.0,414.l.474186.t.4,2,8.0,True +178,33403,Mac Jones,False,https://s.yimg.com/iu/api/res/1.2/VVXTgj1Tqq52qPX_2eEPFA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09122022/33403.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,151.0,13.0,414.l.474186.t.4,2,12.0,True +179,30142,David Njoku,False,https://s.yimg.com/iu/api/res/1.2/eGmGNbYH6BDTB6ZQQnFJQw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09132022/30142.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,138.0,12.0,414.l.474186.t.4,2,11.0,True +180,32814,DeeJay Dallas,False,https://s.yimg.com/iu/api/res/1.2/bGFcLBcr0zcHTeUMUOCsmQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07252023/32814.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,,,,2,10.0,True +181,32858,Tyler Bass,False,https://s.yimg.com/iu/api/res/1.2/bG28u.Fasm8RfRkARyNM.g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08162022/32858.png,K,['K'],K,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,199.0,17.0,414.l.474186.t.4,2,16.0,True +182,100005,Cleveland,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/cle.gif,DT,['DEF'],DEF,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,,,,2,10.0,True +183,100004,Cincinnati,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/cin_2.gif,DT,['DEF'],BN,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,210.0,18.0,414.l.474186.t.4,2,17.0,True +184,100016,Minnesota,False,https://s.yimg.com/xe/ipt/50x50w.6.gif,DT,['DEF'],BN,414.l.474186.t.4,Show Me the Mooney,Jeff,,2022,,,,2,10.0,True +185,27564,Derek Carr,True,https://s.yimg.com/iu/api/res/1.2/k2EXQR3Hz7GtngIYEn3VIQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08082023/27564.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,121.0,11.0,414.l.474186.t.9,0,10.0,False +186,28392,Amari Cooper,False,https://s.yimg.com/iu/api/res/1.2/T2xt0gxTthA0vH6ako9vrw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09132022/28392.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,Q,2022,80.0,7.0,414.l.474186.t.9,2,6.0,True +187,32727,Van Jefferson,False,https://s.yimg.com/iu/api/res/1.2/nOtC5FZStdnLKxi6eX9H9Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/32727.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,,,,2,10.0,True +188,31010,Courtland Sutton,True,https://s.yimg.com/iu/api/res/1.2/qjs5FP7W_un0vxmqlUowUQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08262022/31010.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,73.0,7.0,414.l.474186.t.9,1,6.0,True +189,34019,James Cook,False,https://s.yimg.com/iu/api/res/1.2/Kwq2eYBk7s_hzE.QTsZXaA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08102022/34019.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,97.0,9.0,414.l.474186.t.9,2,8.0,True +190,33495,Michael Carter,False,https://s.yimg.com/iu/api/res/1.2/Qm1_5db1YOsD5hWtGXQlUg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/33495.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,96.0,8.0,414.l.474186.t.9,2,7.0,True +191,31056,Mark Andrews,False,https://s.yimg.com/iu/api/res/1.2/y5Rn0Y2I20WZF_AhPnbrIA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08222022/31056.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,25.0,3.0,414.l.474186.t.9,2,2.0,True +192,31394,Jeff Wilson Jr.,False,https://s.yimg.com/iu/api/res/1.2/We7VtHVodVD_TH5jUJmmmg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/11072022/31394.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,168.0,14.0,414.l.474186.t.9,2,13.0,True +193,30125,Deshaun Watson,False,https://s.yimg.com/iu/api/res/1.2/6I6UVE5j_yQKbJvBeWm9zQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09132022/30125.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,195.0,17.0,414.l.474186.t.8,2,10.0,True +194,33473,Amari Rodgers,False,https://s.yimg.com/iu/api/res/1.2/2RZA8J2wPaxhCTkKX07_bQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/11232022/33473.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,,,,2,10.0,True +195,31868,Deebo Samuel,False,https://s.yimg.com/iu/api/res/1.2/OGE5MT8Euxg9JAackPF6pw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09062022/31868.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,24.0,2.0,414.l.474186.t.9,2,1.0,True +196,32711,Jonathan Taylor,False,https://s.yimg.com/iu/api/res/1.2/G7TZVr_1W8fZLPEuYpLwkg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09012022/32711.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,Q,2022,1.0,1.0,414.l.474186.t.9,2,0.0,False +197,34063,Dameon Pierce,False,https://s.yimg.com/iu/api/res/1.2/FOZCdiPoZ3wxE6WNqFVA4g--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/34063.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,Q,2022,48.0,4.0,414.l.474186.t.9,2,3.0,True +198,30227,Samaje Perine,False,https://s.yimg.com/iu/api/res/1.2/t09WyiAn4WAEPdK8fOopUQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08102023/30227.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,179.0,15.0,414.l.474186.t.2,2,10.0,True +199,33423,Javonte Williams,False,https://s.yimg.com/iu/api/res/1.2/u7lRRElEY9wFdB4IDuRPBw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08262022/33423.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,Q,2022,28.0,3.0,414.l.474186.t.6,2,2.0,True +200,30997,Rashaad Penny,True,https://s.yimg.com/iu/api/res/1.2/VaIszPrtdc5voaBaitswxg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08042023/30997.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,119.0,10.0,414.l.474186.t.10,1,9.0,True +201,30346,Harrison Butker,False,https://s.yimg.com/iu/api/res/1.2/kRja96cMmbAodp9m4xmo2Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09052022/30346.png,K,['K'],K,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,212.0,18.0,414.l.474186.t.3,2,10.0,True +202,100033,Baltimore,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/bal_2.gif,DT,['DEF'],DEF,414.l.474186.t.9,Sutton On The Dock Of The Bay,Devin,,2022,185.0,16.0,414.l.474186.t.1,2,10.0,True +203,34042,Malik Willis,False,https://s.yimg.com/iu/api/res/1.2/.htls9vA4b6ZtFaw0_rmbw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08182023/34042.png,O,"['QB', 'Q/W/R/T']",QB,414.l.474186.t.8,Robotron 7000,Ben,,2022,153.0,13.0,414.l.474186.t.11,2,10.0,True +204,27535,Mike Evans,False,https://s.yimg.com/iu/api/res/1.2/.7Fzudy9HGhlMQCxWxBAEg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08182023/27535.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.8,Robotron 7000,Ben,,2022,22.0,2.0,414.l.474186.t.8,2,1.0,True +205,31017,Christian Kirk,False,https://s.yimg.com/iu/api/res/1.2/Wom.mgKC6gbvZILJMAEiJQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/31017.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.8,Robotron 7000,Ben,Q,2022,75.0,7.0,414.l.474186.t.8,2,6.0,True +206,33963,Drake London,False,https://s.yimg.com/iu/api/res/1.2/I2R4uFJcaejHiOMt_q6tIw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08192022/33963.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",WR,414.l.474186.t.8,Robotron 7000,Ben,,2022,104.0,9.0,414.l.474186.t.1,2,8.0,True +207,31856,Josh Jacobs,False,https://s.yimg.com/iu/api/res/1.2/6q.PFWRMhuvredK21Mel5Q--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/31856.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.8,Robotron 7000,Ben,,2022,51.0,5.0,414.l.474186.t.8,2,4.0,True +208,30121,Christian McCaffrey,False,https://s.yimg.com/iu/api/res/1.2/NMM4SpK6RcmQaRbcmxcRUA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/07262023/30121.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",RB,414.l.474186.t.8,Robotron 7000,Ben,,2022,3.0,1.0,414.l.474186.t.8,2,0.0,False +209,30157,Gerald Everett,False,https://s.yimg.com/iu/api/res/1.2/X6vxfBP9CfXbn3X6T5.ulg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/30157.png,O,"['TE', 'W/R/T', 'Q/W/R/T']",TE,414.l.474186.t.8,Robotron 7000,Ben,,2022,,,,2,10.0,True +210,31908,Terry McLaurin,False,https://s.yimg.com/iu/api/res/1.2/6ubX.FQD1vkEgx_QRp0BIQ--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08172023/31908.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",W/R/T,414.l.474186.t.8,Robotron 7000,Ben,,2022,27.0,3.0,414.l.474186.t.8,2,2.0,True +211,32676,Justin Herbert,True,https://s.yimg.com/iu/api/res/1.2/MkjpSwRr3oxLnp364DoFQw--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09092022/32676.png,O,"['QB', 'Q/W/R/T']",Q/W/R/T,414.l.474186.t.8,Robotron 7000,Ben,Q,2022,94.0,8.0,414.l.474186.t.8,0,7.0,False +212,31141,Mike White,False,https://s.yimg.com/iu/api/res/1.2/Pipjb9dyDTzyvf1DNmvaQg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08102023/31141.png,O,"['QB', 'Q/W/R/T']",BN,414.l.474186.t.8,Robotron 7000,Ben,,2022,,,,2,10.0,True +213,34120,Kyren Williams,False,https://s.yimg.com/iu/api/res/1.2/eXr3FPJ36vxaxtgECBvrrA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09082022/34120.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.8,Robotron 7000,Ben,,2022,,,,2,10.0,True +214,34088,Romeo Doubs,False,https://s.yimg.com/iu/api/res/1.2/PNVAqx8vKPCkvep7QF55xg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08302022/34088.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.8,Robotron 7000,Ben,,2022,142.0,12.0,414.l.474186.t.8,2,11.0,True +215,34009,Skyy Moore,False,https://s.yimg.com/iu/api/res/1.2/UNwknWtbcShl2pH_EzW.9A--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/34009.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.8,Robotron 7000,Ben,,2022,159.0,14.0,414.l.474186.t.7,2,10.0,True +216,33967,Jameson Williams,False,https://s.yimg.com/iu/api/res/1.2/Yf4GNRg6hfpOwcphhsWmuA--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/08302022/33967.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.8,Robotron 7000,Ben,SUSP,2022,190.0,16.0,414.l.474186.t.8,2,15.0,True +217,30996,Calvin Ridley,False,https://s.yimg.com/iu/api/res/1.2/YiOVcPN_6AWTwPob4gWpRg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/12072022/30996.png,O,"['WR', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.8,Robotron 7000,Ben,,2022,,,,2,10.0,True +218,34078,Zamir White,False,https://s.yimg.com/iu/api/res/1.2/4ynGBO1xwnxQipSY6Gszbg--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09022022/34078.png,O,"['RB', 'W/R/T', 'Q/W/R/T']",BN,414.l.474186.t.8,Robotron 7000,Ben,,2022,,,,2,10.0,True +219,27369,Brett Maher,False,https://s.yimg.com/iu/api/res/1.2/Jd6XOuB8nwONFYN9ZDnkog--~C/YXBwaWQ9eXNwb3J0cztjaD0yMzM2O2NyPTE7Y3c9MTc5MDtkeD04NTc7ZHk9MDtmaT11bGNyb3A7aD02MDtxPTEwMDt3PTQ2/https://s.yimg.com/xe/i/us/sp/v/nfl_cutout/players_l/09222022/27369.png,K,['K'],K,414.l.474186.t.8,Robotron 7000,Ben,,2022,,,,2,10.0,True +220,100020,New York,False,https://s.yimg.com/lq/i/us/sp/v/nfl/teams/1/50x50w/nyj.gif,DT,['DEF'],DEF,414.l.474186.t.8,Robotron 7000,Ben,,2022,,,,2,10.0,True diff --git a/tests/run_tests.sh b/tests/run_tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..a3d5bca44b256079b6265d548204d001fc286236 --- /dev/null +++ b/tests/run_tests.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Arguments for each test +BLACK_ARGS="--check" +RUFF_ARGS="check" +MYPY_ARGS="" +PYTEST_ARGS="" + +# Use -f for +while getopts 'f' OPTION; do + case "$OPTION" in + f) + echo "Fix mode" + BLACK_ARGS="" + RUFF_ARGS+=" --fix" + ;; + esac +done +shift "$(($OPTIND -1))" + +testheader () { + echo -e '\n' + echo "*"$emptyvar{1..20} + echo $1 + echo "*"$emptyvar{1..20} +} + +APPDIR=/app/src +TESTSDIR=/app/tests + +testheader "black" +black $BLACK_ARGS $APPDIR $TESTSDIR + +testheader "ruff" +ruff $RUFF_ARGS $APPDIR $TESTSDIR + +testheader "mypy" +mypy $MYPY_ARGS $APPDIR $TESTSDIR + +testheader "pytest" +pytest $PYTEST_ARGS $TESTSDIR/unit \ No newline at end of file diff --git a/tests/unit/test_empty_example.py b/tests/unit/test_empty_example.py new file mode 100644 index 0000000000000000000000000000000000000000..a3e981b51f30e3de506d8f4d04287c632f1a56f1 --- /dev/null +++ b/tests/unit/test_empty_example.py @@ -0,0 +1,2 @@ +def test_nothing(): + pass diff --git a/update_streamlit_version.py b/update_streamlit_version.py new file mode 100644 index 0000000000000000000000000000000000000000..704666862907e5efb07b2fe29f8121eb4f00a582 --- /dev/null +++ b/update_streamlit_version.py @@ -0,0 +1,22 @@ +import pathlib +import pkg_resources +import re + + +package_name = "streamlit" + +with pathlib.Path("requirements.txt").open() as requirements_txt: + for requirement in pkg_resources.parse_requirements(requirements_txt): + if requirement.name == package_name: + version = requirement.specs[0][-1] + break +print(version) + + +with open("README.md", "r") as f: + contents = f.read() + replaced = re.sub(r"sdk_version:.*", f"sdk_version: {version}", contents, 1) + + +with open("README.md", "w") as f: + f.write(replaced)