vlrdevapi 2.0.0__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/CHANGELOG.md +61 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/PKG-INFO +1 -1
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/pyproject.toml +1 -1
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/__init__.py +1 -1
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/info/parser.py +2 -3
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/list/models.py +10 -2
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/namespace.py +6 -2
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/stages/parser.py +4 -6
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/teams/__init__.py +2 -1
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/teams/models.py +22 -1
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/teams/namespace.py +4 -2
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/teams/parser.py +30 -3
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/_common_parsing.py +6 -8
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/agents/models.py +0 -2
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/agents/parser.py +1 -1
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/profile/parser.py +1 -1
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/players/parser.py +112 -94
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/commons/datetime.py +20 -11
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/commons/timezone.py +9 -2
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/uv.lock +241 -229
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.gitattributes +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.github/workflows/docs-check.yml +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.github/workflows/publish.yml +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.github/workflows/test.yml +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.gitignore +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.python-version +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/.readthedocs.yaml +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/CODE_OF_CONDUCT.md +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/CONTRIBUTING.md +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/LICENCE +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/README.md +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/SECURITY.md +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/SUPPORT.md +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/scripts/check_mdx_examples.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/scripts/download_fixtures.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/__init__.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_base.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_cache.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_client.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/_common.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/info/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/info/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/info/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/info/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/list/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/list/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/list/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/list/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/matches/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/matches/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/matches/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/matches/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/matches/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/stages/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/stages/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/stages/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/stages/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/standings/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/standings/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/standings/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/standings/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/standings/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_event/teams/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/common.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/completed/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/completed/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/completed/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/completed/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/completed/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/live/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/live/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/live/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/live/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/live/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/upcoming/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/upcoming/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/upcoming/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/upcoming/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_matches/upcoming/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/agents/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/agents/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/agents/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/info/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/info/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/info/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/info/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/info/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/matches/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/matches/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/matches/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/matches/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/matches/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/profile/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/profile/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/profile/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/profile/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/teams/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/teams/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/teams/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/teams/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_player/teams/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/economy/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/economy/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/economy/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/economy/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/economy/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/info/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/info/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/info/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/info/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/info/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/match_namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/match_namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/performance/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/performance/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/performance/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/performance/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/performance/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/players/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/players/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/players/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/players/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/rounds/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/rounds/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/rounds/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/rounds/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/rounds/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/vods/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/vods/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/vods/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/vods/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_series/vods/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/completed_matches/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/completed_matches/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/completed_matches/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/completed_matches/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/completed_matches/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/info/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/info/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/info/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/info/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/info/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/placements/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/placements/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/placements/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/placements/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/placements/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/roster/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/roster/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/roster/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/roster/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/roster/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/stats/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/stats/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/stats/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/stats/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/stats/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/transactions/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/transactions/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/transactions/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/transactions/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/transactions/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/upcoming_matches/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/upcoming_matches/models.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/upcoming_matches/namespace.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/upcoming_matches/namespace.pyi +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_team/upcoming_matches/parser.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_utils/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_utils/pagination.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_utils/paths.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_utils/team_enrichment.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/_utils/team_parsing.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/commons/__init__.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/commons/countries.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/commons/mappings.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/commons/prizes.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/exceptions.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/fetcher.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/py.typed +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/src/vlrdevapi/validators.py +0 -0
- {vlrdevapi-2.0.0 → vlrdevapi-2.1.0}/wrangler.jsonc +0 -0
|
@@ -5,6 +5,65 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Events list dates without a year** — `event.list()` dates are rendered
|
|
13
|
+
on vlr.gg without a year (e.g. `"Jul 9 – Aug 23"`). These now use a
|
|
14
|
+
sentinel year of `2019` instead of the current year. Any parsed date with
|
|
15
|
+
`year < 2020` means the source omitted the year, so guard with
|
|
16
|
+
`event.start_date.year < 2020` before relying on it.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Python 3.13/3.15 date parsing deprecation** — `datetime.strptime` emits
|
|
21
|
+
a `DeprecationWarning` when parsing a day-of-month without a year (and will
|
|
22
|
+
change behavior in Python 3.15). All such parses now supply an explicit
|
|
23
|
+
year derived from context (reference date, page-header year, or the
|
|
24
|
+
sentinel above).
|
|
25
|
+
|
|
26
|
+
## [2.1.0] - 2026-08-07
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **Event teams player rosters** — `event.teams()` now includes a `players`
|
|
31
|
+
list on each `Team`, containing each player's `name` and `id` parsed from
|
|
32
|
+
the event page. The `players` field is always present and defaults to `[]`
|
|
33
|
+
when roster data is unavailable. A new `TeamPlayer` model was added and exposed
|
|
34
|
+
from the `_event.teams` submodule.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- **Documentation** — event teams reference updated with the new `players`
|
|
39
|
+
field, a `TeamPlayer` fields table, and a roster iteration example.
|
|
40
|
+
|
|
41
|
+
## [2.0.1] - 2026-07-29
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **Series player stats** — overview tab HTML changed from `<table>` to
|
|
46
|
+
`<div>` on vlr.gg; updated CSS selectors to match the new structure.
|
|
47
|
+
- **Player agent stats** — agent table class changed from `wf-table` to
|
|
48
|
+
`st-table.mod-agent-rows`; column count reduced from 17 to 16 (FKPR and
|
|
49
|
+
FDPR columns consolidated into a single FK:FD ratio column).
|
|
50
|
+
- **Player profile** — same agent table selector fix; the profile fallback
|
|
51
|
+
to `timespan=all` now works correctly.
|
|
52
|
+
- **Team fixtures** — updated stale test assertions for NRG Haven map
|
|
53
|
+
stats (games played, wins, losses, etc.) and total winnings to match
|
|
54
|
+
current vlr.gg data.
|
|
55
|
+
|
|
56
|
+
### Removed
|
|
57
|
+
|
|
58
|
+
- `fkpr` and `fdpr` fields from `AgentStats` model — vlr.gg no longer
|
|
59
|
+
exposes separate First Kills Per Round / First Deaths Per Round columns.
|
|
60
|
+
The `first_kills` and `first_deaths` total fields remain available.
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- **Documentation** — agents schema reference updated to reflect the
|
|
65
|
+
removal of `fkpr` and `fdpr`.
|
|
66
|
+
|
|
8
67
|
## [2.0.0] - 2026-07-07
|
|
9
68
|
|
|
10
69
|
### Major Rewrite
|
|
@@ -66,4 +125,6 @@ The API surface, module structure, and type system are all new.
|
|
|
66
125
|
- `aiohttp` and `asyncio` dependencies.
|
|
67
126
|
- v1.x match/event/team parsing modules.
|
|
68
127
|
|
|
128
|
+
[2.1.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.0.1...v2.1.0
|
|
129
|
+
[2.0.1]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.0.0...v2.0.1
|
|
69
130
|
[2.0.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v1.6.2...v2.0.0
|
|
@@ -288,10 +288,9 @@ def _parse_partial_date(text: str, reference_end: date) -> date | None:
|
|
|
288
288
|
if full_attempt is not None:
|
|
289
289
|
return full_attempt
|
|
290
290
|
|
|
291
|
-
for fmt in ("%b %d", "%B %d"):
|
|
291
|
+
for fmt in ("%b %d %Y", "%B %d %Y"):
|
|
292
292
|
try:
|
|
293
|
-
|
|
294
|
-
return dt.replace(year=reference_end.year).date()
|
|
293
|
+
return datetime.strptime(f"{text} {reference_end.year}", fmt).date()
|
|
295
294
|
except ValueError:
|
|
296
295
|
continue
|
|
297
296
|
|
|
@@ -22,8 +22,16 @@ class EventListItem(BaseModel):
|
|
|
22
22
|
default="upcoming", description="Event status",
|
|
23
23
|
)
|
|
24
24
|
prize: EventPrize | None = Field(default=None, description="Prize information")
|
|
25
|
-
start_date: date | None = Field(
|
|
26
|
-
|
|
25
|
+
start_date: date | None = Field(
|
|
26
|
+
default=None,
|
|
27
|
+
description="Event start date. Year is 2019 when the listing omitted it "
|
|
28
|
+
"(year < 2020 means the year is not present).",
|
|
29
|
+
)
|
|
30
|
+
end_date: date | None = Field(
|
|
31
|
+
default=None,
|
|
32
|
+
description="Event end date. Year is 2019 when the listing omitted it "
|
|
33
|
+
"(year < 2020 means the year is not present).",
|
|
34
|
+
)
|
|
27
35
|
region: str = Field(default="", description="Country or region name from flag")
|
|
28
36
|
image_url: str = Field(default="", description="URL to the event thumbnail image")
|
|
29
37
|
url: str = Field(default="", description="Full URL to the event page on vlr.gg")
|
|
@@ -99,8 +99,10 @@ class EventMatchNamespace:
|
|
|
99
99
|
If ``None``, returns teams for all stages.
|
|
100
100
|
|
|
101
101
|
Returns:
|
|
102
|
-
EventTeams: Teams grouped by stage, with team ``name``, ``
|
|
103
|
-
``
|
|
102
|
+
EventTeams: Teams grouped by stage, with team ``name``, ``id``,
|
|
103
|
+
optional ``note``, and a ``players`` list (each with ``name``
|
|
104
|
+
and ``id``). ``players`` is always present and may be an empty
|
|
105
|
+
list when roster data is unavailable.
|
|
104
106
|
|
|
105
107
|
Raises:
|
|
106
108
|
NotFoundError: If the event page does not exist (HTTP 404).
|
|
@@ -241,6 +243,8 @@ class EventNamespace:
|
|
|
241
243
|
Returns:
|
|
242
244
|
An ``EventTeamsNamespace`` instance. Call with an ``event_id``
|
|
243
245
|
and optional ``stage`` to return an ``EventTeams`` model.
|
|
246
|
+
Each ``Team`` includes a ``players`` roster list that is always
|
|
247
|
+
present and may be empty when roster data is unavailable.
|
|
244
248
|
|
|
245
249
|
"""
|
|
246
250
|
return self._teams
|
|
@@ -189,10 +189,9 @@ def _parse_subnav_single_date(text: str, year: int | None) -> date | None:
|
|
|
189
189
|
return None
|
|
190
190
|
|
|
191
191
|
if year is not None:
|
|
192
|
-
for fmt in ("%b %d", "%B %d"):
|
|
192
|
+
for fmt in ("%b %d %Y", "%B %d %Y"):
|
|
193
193
|
try:
|
|
194
|
-
|
|
195
|
-
return dt.replace(year=year).date()
|
|
194
|
+
return datetime.strptime(f"{text} {year}", fmt).date()
|
|
196
195
|
except ValueError:
|
|
197
196
|
continue
|
|
198
197
|
|
|
@@ -220,10 +219,9 @@ def _parse_subnav_partial_date(text: str, year: int) -> date | None:
|
|
|
220
219
|
if full is not None:
|
|
221
220
|
return full
|
|
222
221
|
|
|
223
|
-
for fmt in ("%b %d", "%B %d"):
|
|
222
|
+
for fmt in ("%b %d %Y", "%B %d %Y"):
|
|
224
223
|
try:
|
|
225
|
-
|
|
226
|
-
return dt.replace(year=year).date()
|
|
224
|
+
return datetime.strptime(f"{text} {year}", fmt).date()
|
|
227
225
|
except ValueError:
|
|
228
226
|
continue
|
|
229
227
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from vlrdevapi._event.teams.models import EventStageTeams, EventTeams
|
|
1
|
+
from vlrdevapi._event.teams.models import EventStageTeams, EventTeams, TeamPlayer
|
|
2
2
|
from vlrdevapi._event.teams.namespace import EventTeamsNamespace
|
|
3
3
|
|
|
4
4
|
__all__ = [
|
|
5
5
|
"EventStageTeams",
|
|
6
6
|
"EventTeams",
|
|
7
|
+
"TeamPlayer",
|
|
7
8
|
"EventTeamsNamespace",
|
|
8
9
|
]
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
from pydantic import BaseModel, ConfigDict, Field
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
class TeamPlayer(BaseModel):
|
|
5
|
+
"""Player information for a team at an event."""
|
|
6
|
+
|
|
7
|
+
model_config = ConfigDict(
|
|
8
|
+
json_schema_extra={"description": "Player information for a team at an event."},
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
id: int = Field(
|
|
12
|
+
default=0,
|
|
13
|
+
description="Player identifier from vlr.gg (e.g., 1311)",
|
|
14
|
+
)
|
|
15
|
+
name: str = Field(
|
|
16
|
+
default="",
|
|
17
|
+
description="Player in-game name (e.g., 'Melser')",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
4
21
|
class Team(BaseModel):
|
|
5
22
|
"""Team information for an event from vlr.gg."""
|
|
6
23
|
|
|
@@ -20,6 +37,10 @@ class Team(BaseModel):
|
|
|
20
37
|
default=None,
|
|
21
38
|
description="Optional note (e.g., 'Partner Team')",
|
|
22
39
|
)
|
|
40
|
+
players: list[TeamPlayer] = Field(
|
|
41
|
+
default_factory=list,
|
|
42
|
+
description="List of players on the team (name and id). Always present; empty when roster data is unavailable.",
|
|
43
|
+
)
|
|
23
44
|
|
|
24
45
|
|
|
25
46
|
class EventStageTeams(BaseModel):
|
|
@@ -60,4 +81,4 @@ class EventTeams(BaseModel):
|
|
|
60
81
|
return iter(self.stages)
|
|
61
82
|
|
|
62
83
|
def __getitem__(self, index):
|
|
63
|
-
return self.stages[index]
|
|
84
|
+
return self.stages[index]
|
|
@@ -43,8 +43,10 @@ class EventTeamsNamespace:
|
|
|
43
43
|
stage: Optional stage name or path to filter by (e.g. 'playoffs').
|
|
44
44
|
|
|
45
45
|
Returns:
|
|
46
|
-
EventTeams: Teams grouped by stage, with team ``name``, ``
|
|
47
|
-
``
|
|
46
|
+
EventTeams: Teams grouped by stage, with team ``name``, ``id``,
|
|
47
|
+
optional ``note``, and a ``players`` list (each with ``name``
|
|
48
|
+
and ``id``). ``players`` is always present and may be an empty
|
|
49
|
+
list when roster data is unavailable.
|
|
48
50
|
|
|
49
51
|
Raises:
|
|
50
52
|
ValidationError: If ``event_id`` is not a valid positive integer.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
1
|
from selectolax.parser import HTMLParser, Node
|
|
3
2
|
|
|
4
|
-
from vlrdevapi._event.teams.models import Team
|
|
3
|
+
from vlrdevapi._event.teams.models import Team, TeamPlayer
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
def parse_subnav(html: HTMLParser) -> list[tuple[str, str]]:
|
|
@@ -70,4 +69,32 @@ def _parse_team_card(card: Node) -> Team | None:
|
|
|
70
69
|
note_el = card.css_first(".event-team-note")
|
|
71
70
|
note = note_el.text(strip=True) if note_el else None
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
players = _parse_players(card)
|
|
73
|
+
|
|
74
|
+
return Team(name=name, id=team_id, note=note, players=players)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _parse_players(card: Node) -> list[TeamPlayer]:
|
|
78
|
+
"""Parse the players listed on a team card."""
|
|
79
|
+
players = []
|
|
80
|
+
for item in card.css(".event-team-players-item"):
|
|
81
|
+
player = _parse_player_item(item)
|
|
82
|
+
if player:
|
|
83
|
+
players.append(player)
|
|
84
|
+
return players
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _parse_player_item(item: Node) -> TeamPlayer | None:
|
|
88
|
+
"""Parse a single player anchor (e.g. /player/1311/melser)."""
|
|
89
|
+
href = item.attributes.get("href", "")
|
|
90
|
+
if not href or "/player/" not in href:
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
id_str = href.split("/player/")[1].split("/")[0]
|
|
95
|
+
player_id = int(id_str)
|
|
96
|
+
except (ValueError, IndexError):
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
name = item.text(strip=True)
|
|
100
|
+
return TeamPlayer(id=player_id, name=name)
|
|
@@ -139,7 +139,7 @@ def _parse_agent_row(tr: Node) -> AgentStats:
|
|
|
139
139
|
stats = AgentStats()
|
|
140
140
|
tds = tr.css("td")
|
|
141
141
|
|
|
142
|
-
if len(tds) <
|
|
142
|
+
if len(tds) < 16:
|
|
143
143
|
return stats
|
|
144
144
|
|
|
145
145
|
img_el = tds[0].css_first("img")
|
|
@@ -160,12 +160,10 @@ def _parse_agent_row(tr: Node) -> AgentStats:
|
|
|
160
160
|
stats.kast = tds[7].text(strip=True)
|
|
161
161
|
stats.kpr = _parse_float(tds[8].text(strip=True))
|
|
162
162
|
stats.apr = _parse_float(tds[9].text(strip=True))
|
|
163
|
-
stats.
|
|
164
|
-
stats.
|
|
165
|
-
stats.
|
|
166
|
-
stats.
|
|
167
|
-
stats.
|
|
168
|
-
stats.first_kills = _parse_int(tds[15].text(strip=True))
|
|
169
|
-
stats.first_deaths = _parse_int(tds[16].text(strip=True))
|
|
163
|
+
stats.kills = _parse_int(tds[11].text(strip=True))
|
|
164
|
+
stats.deaths = _parse_int(tds[12].text(strip=True))
|
|
165
|
+
stats.assists = _parse_int(tds[13].text(strip=True))
|
|
166
|
+
stats.first_kills = _parse_int(tds[14].text(strip=True))
|
|
167
|
+
stats.first_deaths = _parse_int(tds[15].text(strip=True))
|
|
170
168
|
|
|
171
169
|
return stats
|
|
@@ -18,8 +18,6 @@ class AgentStats(BaseModel):
|
|
|
18
18
|
kast: str = Field(default="", description="Kill, Assist, Survive, Trade %")
|
|
19
19
|
kpr: float | None = Field(default=None, description="Kills Per Round")
|
|
20
20
|
apr: float | None = Field(default=None, description="Assists Per Round")
|
|
21
|
-
fkpr: float | None = Field(default=None, description="First Kills Per Round")
|
|
22
|
-
fdpr: float | None = Field(default=None, description="First Deaths Per Round")
|
|
23
21
|
kills: int | None = Field(default=None, description="Total kills")
|
|
24
22
|
deaths: int | None = Field(default=None, description="Total deaths")
|
|
25
23
|
assists: int | None = Field(default=None, description="Total assists")
|
|
@@ -16,7 +16,7 @@ def parse_agent_stats(html: HTMLParser, timespan: str = "all") -> AgentStatsPage
|
|
|
16
16
|
card = _next_sibling_card(search_from)
|
|
17
17
|
if card is None:
|
|
18
18
|
break
|
|
19
|
-
table = card.css_first("table.
|
|
19
|
+
table = card.css_first("table.st-table.mod-agent-rows")
|
|
20
20
|
if table is None:
|
|
21
21
|
break
|
|
22
22
|
tbody = table.css_first("tbody")
|
|
@@ -69,7 +69,7 @@ def _parse_agents(html: HTMLParser, profile: PlayerProfile) -> None:
|
|
|
69
69
|
card = _next_sibling_card(search_from)
|
|
70
70
|
if card is None:
|
|
71
71
|
break
|
|
72
|
-
table = card.css_first("table.
|
|
72
|
+
table = card.css_first("table.st-table.mod-agent-rows")
|
|
73
73
|
if table is None:
|
|
74
74
|
break
|
|
75
75
|
tbody = table.css_first("tbody")
|
|
@@ -138,119 +138,137 @@ def _parse_stat_cell(td: Node) -> tuple:
|
|
|
138
138
|
return overall, attack, defend
|
|
139
139
|
|
|
140
140
|
|
|
141
|
-
def _parse_player_row(
|
|
142
|
-
|
|
141
|
+
def _parse_player_row(cell: Node) -> PlayerGameStats:
|
|
142
|
+
player = PlayerGameStats()
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
a = cell.css_first("a")
|
|
145
|
+
if a:
|
|
146
|
+
href = a.attributes.get("href", "") or ""
|
|
147
|
+
parts = href.strip("/").split("/")
|
|
148
|
+
if len(parts) >= 2:
|
|
149
|
+
with contextlib.suppress(ValueError):
|
|
150
|
+
player.player_id = int(parts[1])
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
td_player = tds[0] if len(tds) > 0 else None
|
|
156
|
-
td_agents = tds[1] if len(tds) > 1 else None
|
|
157
|
-
|
|
158
|
-
if td_player:
|
|
159
|
-
a = td_player.css_first("a")
|
|
160
|
-
if a:
|
|
161
|
-
href = a.attributes.get("href", "") or ""
|
|
162
|
-
parts = href.strip("/").split("/")
|
|
163
|
-
if len(parts) >= 2:
|
|
164
|
-
with contextlib.suppress(ValueError):
|
|
165
|
-
player.player_id = int(parts[1])
|
|
166
|
-
|
|
167
|
-
name_el = td_player.css_first(".text-of")
|
|
168
|
-
if name_el:
|
|
169
|
-
player.name = name_el.text(strip=True)
|
|
152
|
+
name_el = cell.css_first(".ovw-player-name")
|
|
153
|
+
if name_el:
|
|
154
|
+
player.name = name_el.text(strip=True)
|
|
155
|
+
else:
|
|
156
|
+
bold_el = cell.css_first("div[style*='font-weight: 700']")
|
|
157
|
+
if bold_el:
|
|
158
|
+
player.name = bold_el.text(strip=True)
|
|
170
159
|
else:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
for div in td_player.css("div"):
|
|
176
|
-
text = div.text(strip=True)
|
|
177
|
-
if text and len(text) <= 20 and not div.css("div"):
|
|
178
|
-
player.name = text
|
|
179
|
-
break
|
|
180
|
-
|
|
181
|
-
team_el = td_player.css_first(".ge-text-light")
|
|
182
|
-
if team_el:
|
|
183
|
-
player.team_short = team_el.text(strip=True)
|
|
184
|
-
|
|
185
|
-
flag_el = td_player.css_first("i.flag")
|
|
186
|
-
if flag_el:
|
|
187
|
-
for cls in (flag_el.attributes.get("class") or "").split():
|
|
188
|
-
if cls.startswith("mod-") and cls != "mod-none":
|
|
189
|
-
player.country_code = cls[4:]
|
|
190
|
-
player.country = get_country_name(player.country_code)
|
|
160
|
+
for div in cell.css("div"):
|
|
161
|
+
text = div.text(strip=True)
|
|
162
|
+
if text and len(text) <= 20 and not div.css("div"):
|
|
163
|
+
player.name = text
|
|
191
164
|
break
|
|
192
165
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if alt:
|
|
197
|
-
player.agents.append(alt.title())
|
|
166
|
+
team_el = cell.css_first(".ovw-player-tag")
|
|
167
|
+
if team_el:
|
|
168
|
+
player.team_short = team_el.text(strip=True)
|
|
198
169
|
|
|
199
|
-
|
|
170
|
+
flag_el = cell.css_first("i.flag")
|
|
171
|
+
if flag_el:
|
|
172
|
+
for cls in (flag_el.attributes.get("class") or "").split():
|
|
173
|
+
if cls.startswith("mod-") and cls != "mod-none":
|
|
174
|
+
player.country_code = cls[4:]
|
|
175
|
+
player.country = get_country_name(player.country_code)
|
|
176
|
+
break
|
|
200
177
|
|
|
178
|
+
for img in cell.css(".ovw-agents img"):
|
|
179
|
+
alt = img.attributes.get("alt", "") or ""
|
|
180
|
+
if alt:
|
|
181
|
+
player.agents.append(alt.title())
|
|
201
182
|
|
|
202
|
-
|
|
203
|
-
"""Parse an entire player stats table into PlayerGameStats list.
|
|
183
|
+
return player
|
|
204
184
|
|
|
205
|
-
Args:
|
|
206
|
-
table: The table Node containing player stat rows.
|
|
207
185
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
186
|
+
def _parse_kda_cell(cell: Node) -> tuple:
|
|
187
|
+
kills_o = kills_a = kills_d = None
|
|
188
|
+
deaths_o = deaths_a = deaths_d = None
|
|
189
|
+
assists_o = assists_a = assists_d = None
|
|
211
190
|
|
|
212
|
-
""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
191
|
+
for stat_span in cell.css("span.ovw-kda-stat"):
|
|
192
|
+
col = stat_span.attributes.get("data-col", "")
|
|
193
|
+
both_el = stat_span.css_first("span.mod-both")
|
|
194
|
+
t_el = stat_span.css_first("span.mod-t")
|
|
195
|
+
ct_el = stat_span.css_first("span.mod-ct")
|
|
196
|
+
both = _parse_int(both_el.text(strip=True)) if both_el is not None else None
|
|
197
|
+
t = _parse_int(t_el.text(strip=True)) if t_el is not None else None
|
|
198
|
+
ct = _parse_int(ct_el.text(strip=True)) if ct_el is not None else None
|
|
199
|
+
if col == "kills":
|
|
200
|
+
kills_o, kills_a, kills_d = both, t, ct
|
|
201
|
+
elif col == "deaths":
|
|
202
|
+
deaths_o, deaths_a, deaths_d = both, t, ct
|
|
203
|
+
elif col == "assists":
|
|
204
|
+
assists_o, assists_a, assists_d = both, t, ct
|
|
219
205
|
|
|
220
|
-
|
|
206
|
+
return (kills_o, kills_a, kills_d, deaths_o, deaths_a, deaths_d, assists_o, assists_a, assists_d)
|
|
221
207
|
|
|
222
|
-
stat_tds = tds[2:14]
|
|
223
208
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
209
|
+
def _parse_table(table: Node) -> list[PlayerGameStats]:
|
|
210
|
+
players: list[PlayerGameStats] = []
|
|
211
|
+
rows = table.css("div.ovw-row")
|
|
212
|
+
for row in rows:
|
|
213
|
+
cls = row.attributes.get("class", "") or ""
|
|
214
|
+
if "mod-head" in cls:
|
|
215
|
+
continue
|
|
227
216
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
attack_vals.append(a)
|
|
232
|
-
defend_vals.append(d)
|
|
217
|
+
cells = row.css("div.ovw-cell")
|
|
218
|
+
if len(cells) < 11:
|
|
219
|
+
continue
|
|
233
220
|
|
|
234
|
-
|
|
221
|
+
player = _parse_player_row(cells[0])
|
|
222
|
+
|
|
223
|
+
rating_vals = _parse_stat_cell(cells[1])
|
|
224
|
+
acs_vals = _parse_stat_cell(cells[2])
|
|
225
|
+
kills_vals, deaths_vals, assists_vals = None, None, None
|
|
226
|
+
kda_vals = _parse_kda_cell(cells[3])
|
|
227
|
+
kills_vals = (kda_vals[0], kda_vals[1], kda_vals[2])
|
|
228
|
+
deaths_vals = (kda_vals[3], kda_vals[4], kda_vals[5])
|
|
229
|
+
assists_vals = (kda_vals[6], kda_vals[7], kda_vals[8])
|
|
230
|
+
kd_diff_vals = _parse_stat_cell(cells[4])
|
|
231
|
+
kast_vals = _parse_stat_cell(cells[5])
|
|
232
|
+
adr_vals = _parse_stat_cell(cells[6])
|
|
233
|
+
hs_percent_vals = _parse_stat_cell(cells[7])
|
|
234
|
+
first_kills_vals = _parse_stat_cell(cells[8])
|
|
235
|
+
first_deaths_vals = _parse_stat_cell(cells[9])
|
|
236
|
+
fk_fd_diff_vals = _parse_stat_cell(cells[10])
|
|
237
|
+
|
|
238
|
+
def _build_side(
|
|
239
|
+
rating_t, acs_t, kills_t, deaths_t, assists_t, kd_diff_t, kast_t, adr_t, hs_percent_t, first_kills_t, first_deaths_t, fk_fd_diff_t,
|
|
240
|
+
) -> SideStats:
|
|
235
241
|
return SideStats(
|
|
236
|
-
rating=
|
|
237
|
-
acs=
|
|
238
|
-
kills=
|
|
239
|
-
deaths=
|
|
240
|
-
assists=
|
|
241
|
-
kd_diff=
|
|
242
|
-
kast=
|
|
243
|
-
adr=
|
|
244
|
-
hs_percent=
|
|
245
|
-
first_kills=
|
|
246
|
-
first_deaths=
|
|
247
|
-
fk_fd_diff=
|
|
242
|
+
rating=rating_t,
|
|
243
|
+
acs=acs_t,
|
|
244
|
+
kills=kills_t,
|
|
245
|
+
deaths=deaths_t,
|
|
246
|
+
assists=assists_t,
|
|
247
|
+
kd_diff=kd_diff_t,
|
|
248
|
+
kast=kast_t,
|
|
249
|
+
adr=adr_t,
|
|
250
|
+
hs_percent=hs_percent_t,
|
|
251
|
+
first_kills=first_kills_t,
|
|
252
|
+
first_deaths=first_deaths_t,
|
|
253
|
+
fk_fd_diff=fk_fd_diff_t,
|
|
248
254
|
)
|
|
249
255
|
|
|
250
256
|
player.stats = PlayerStats(
|
|
251
|
-
overall=_build_side(
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
overall=_build_side(
|
|
258
|
+
rating_vals[0], acs_vals[0], kills_vals[0], deaths_vals[0], assists_vals[0],
|
|
259
|
+
kd_diff_vals[0], kast_vals[0], adr_vals[0], hs_percent_vals[0],
|
|
260
|
+
first_kills_vals[0], first_deaths_vals[0], fk_fd_diff_vals[0],
|
|
261
|
+
),
|
|
262
|
+
attack=_build_side(
|
|
263
|
+
rating_vals[1], acs_vals[1], kills_vals[1], deaths_vals[1], assists_vals[1],
|
|
264
|
+
kd_diff_vals[1], kast_vals[1], adr_vals[1], hs_percent_vals[1],
|
|
265
|
+
first_kills_vals[1], first_deaths_vals[1], fk_fd_diff_vals[1],
|
|
266
|
+
),
|
|
267
|
+
defend=_build_side(
|
|
268
|
+
rating_vals[2], acs_vals[2], kills_vals[2], deaths_vals[2], assists_vals[2],
|
|
269
|
+
kd_diff_vals[2], kast_vals[2], adr_vals[2], hs_percent_vals[2],
|
|
270
|
+
first_kills_vals[2], first_deaths_vals[2], fk_fd_diff_vals[2],
|
|
271
|
+
),
|
|
254
272
|
)
|
|
255
273
|
|
|
256
274
|
players.append(player)
|
|
@@ -319,7 +337,7 @@ def parse_players_stats(html: HTMLParser, game_id: str = "all") -> PlayersStats:
|
|
|
319
337
|
if not result.map_name:
|
|
320
338
|
result.map_name = map_div.text(strip=True)
|
|
321
339
|
|
|
322
|
-
tables = game_div.css("
|
|
340
|
+
tables = game_div.css("div.ovw-table")
|
|
323
341
|
if len(tables) < 2:
|
|
324
342
|
return result
|
|
325
343
|
|