Python client wrapper for the War Track Dashboard API
Project description
uruwat
Python client wrapper for the War Track Dashboard API. This library provides a type-safe, async-compatible interface for interacting with the War Track Dashboard API, making it easy to query equipment and system data.
Features
- ✅ Type-safe: Full type hints with Pydantic models
- ✅ Async-compatible: Built on httpx for async support
- ✅ Error handling: Custom exception classes for different error scenarios
- ✅ Context manager: Proper resource cleanup with context manager support
- ✅ Well-tested: Comprehensive test suite with high coverage
- ✅ Modern Python: Requires Python 3.10+
Installation
pip install uruwat
Or using uv:
uv add uruwat
Or using poetry:
poetry add uruwat
Quick Start
from uruwat import Client, Country, EquipmentType
# Initialize the client
client = Client(base_url="http://localhost:8000")
# Get equipment data for Ukraine
equipments = client.get_equipments(country=Country.UKRAINE)
# Get equipment with filters
equipments = client.get_equipments(
country=Country.UKRAINE,
types=[EquipmentType.TANKS, EquipmentType.AIRCRAFT],
date_start="2024-01-01",
date_end="2024-12-31",
)
# Get total equipment data
totals = client.get_total_equipments(country=Country.UKRAINE)
# Get system data
from uruwat import Status
systems = client.get_systems(
country=Country.UKRAINE,
status=[Status.DESTROYED, Status.CAPTURED],
)
# Health check
health = client.health_check()
API Reference
Client
The main client class for interacting with the API.
from uruwat import Client
client = Client(
base_url="http://localhost:8000", # Optional, defaults to http://localhost:8000
timeout=30.0, # Optional, defaults to 30.0 seconds
headers={"Authorization": "Bearer token"}, # Optional custom headers
)
Methods
get_equipments()
Get equipment data filtered by country, types, and date range.
Parameters:
country(Country): Country filter (UKRAINE or RUSSIA)types(list[EquipmentType], optional): List of equipment types to filterdate_start(date | str, optional): Start date (YYYY-MM-DD format or date object)date_end(date | str, optional): End date (YYYY-MM-DD format or date object)
Returns: list[Equipment]
Example:
equipments = client.get_equipments(
country=Country.UKRAINE,
types=[EquipmentType.TANKS],
date_start="2024-01-01",
date_end="2024-12-31",
)
get_total_equipments()
Get total equipment data with optional filters.
Parameters:
country(Country, optional): Country filtertypes(list[EquipmentType], optional): List of equipment types to filter
Returns: list[AllEquipment]
Example:
totals = client.get_total_equipments(
country=Country.UKRAINE,
types=[EquipmentType.TANKS],
)
get_equipment_types()
Get distinct equipment types.
Returns: list[dict[str, str]]
Example:
types = client.get_equipment_types()
get_systems()
Get system data filtered by country, systems, status, and date range.
Parameters:
country(Country): Country filter (UKRAINE or RUSSIA)systems(list[str], optional): List of system names to filterstatus(list[Status], optional): List of statuses to filterdate_start(date | str, optional): Start date (YYYY-MM-DD format or date object)date_end(date | str, optional): End date (YYYY-MM-DD format or date object)
Returns: list[System]
Example:
systems = client.get_systems(
country=Country.UKRAINE,
status=[Status.DESTROYED],
date_start="2024-01-01",
date_end="2024-12-31",
)
get_total_systems()
Get total system data with optional filters.
Parameters:
country(Country, optional): Country filtersystems(list[str], optional): List of system names to filter
Returns: list[AllSystem]
Example:
totals = client.get_total_systems(
country=Country.UKRAINE,
systems=["T-72"],
)
get_system_types()
Get distinct system types.
Returns: list[dict[str, str]]
Example:
types = client.get_system_types()
import_equipments()
Trigger import of equipment data from scraper.
Returns: dict[str, str]
import_all_equipments()
Trigger import of all equipment totals from scraper.
Returns: dict[str, str]
import_systems()
Trigger import of system data from scraper.
Returns: dict[str, str]
import_all_systems()
Trigger import of all system totals from scraper.
Returns: dict[str, str]
import_all()
Trigger import of all data from scraper.
Returns: dict[str, str]
health_check()
Check API health status.
Returns: dict[str, str]
Data Models
Equipment
class Equipment:
id: int
country: str
type: str
destroyed: int
abandoned: int
captured: int
damaged: int
total: int
date: str
AllEquipment
class AllEquipment:
id: int
country: str
type: str
destroyed: int
abandoned: int
captured: int
damaged: int
total: int
System
class System:
id: int
country: str
origin: str
system: str
status: str
url: str
date: str
AllSystem
class AllSystem:
id: int
country: str
system: str
destroyed: int
abandoned: int
captured: int
damaged: int
total: int
Enumerations
Country
Country.ALLCountry.UKRAINECountry.RUSSIA
EquipmentType
EquipmentType.TANKSEquipmentType.AIRCRAFTEquipmentType.HELICOPTERS- ... (see full list in code)
Status
Status.DESTROYEDStatus.CAPTUREDStatus.ABANDONEDStatus.DAMAGED
Error Handling
The library provides specific exception classes for different error scenarios:
from uruwat import (
Client,
WarTrackAPIError,
WarTrackAuthenticationError,
WarTrackForbiddenError,
WarTrackNotFoundError,
WarTrackRateLimitError,
WarTrackServerError,
)
client = Client()
try:
equipments = client.get_equipments(country=Country.UKRAINE)
except WarTrackAuthenticationError:
print("Authentication failed")
except WarTrackRateLimitError:
print("Rate limit exceeded")
except WarTrackServerError:
print("Server error")
except WarTrackAPIError as e:
print(f"API error: {e}")
Exception Classes
WarTrackAPIError: Base exception for all API errorsWarTrackAuthenticationError: Raised on 401 UnauthorizedWarTrackForbiddenError: Raised on 403 ForbiddenWarTrackNotFoundError: Raised on 404 Not FoundWarTrackRateLimitError: Raised on 429 Too Many RequestsWarTrackServerError: Raised on 500+ Server Error
Context Manager
The client can be used as a context manager to ensure proper cleanup:
with Client() as client:
equipments = client.get_equipments(country=Country.UKRAINE)
# Client is automatically closed when exiting the context
Development
Setup
# Clone the repository
git clone <repository-url>
cd uruwat
# Install in development mode
uv sync --dev
# Install pre-commit hooks
uv run pre-commit install
Running Tests
# Run all tests (unit tests only, uses mocked requests)
uv run pytest
# Run with coverage
uv run pytest --cov=uruwat --cov-report=html
# Run specific test file
uv run pytest tests/test_client.py
# Run integration tests (requires running API server)
uv run pytest -m integration
Code Quality
# Format code
uv run black .
# Lint code
uv run ruff check .
# Type checking
uv run mypy uruwat
Running CI Checks Locally
You can run the same checks that CI runs locally:
# Run all checks
uv run black --check .
uv run ruff check .
uv run mypy uruwat
uv run pytest
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Workflow
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass and code is formatted (
uv run black . && uv run ruff check . && uv run pytest) - Commit your changes (following Conventional Commits)
- Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Commit Message Guidelines
This project follows Conventional Commits. Commit messages should be formatted as:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Code Style
- This project uses Black for code formatting
- Ruff is used for linting
- mypy is used for type checking
- All code must pass linting and type checking
Releasing
This project uses GitHub Releases to trigger PyPI publishing. The workflow automatically publishes to PyPI when a final (non-pre-release) GitHub Release is created.
Release Workflow
1. Update Version
Update the version in pyproject.toml:
[project]
version = "0.2.0" # Update to your new version
Follow Semantic Versioning:
- MAJOR (1.0.0): Breaking changes
- MINOR (0.1.0): New features, backwards compatible
- PATCH (0.0.1): Bug fixes, backwards compatible
2. Update Changelog
Update CHANGELOG.md with the changes for this version.
3. Commit and Push Changes
git add pyproject.toml CHANGELOG.md
git commit -m "chore: bump version to 0.2.0"
git push origin main
4. Create a Git Tag
Create a tag matching the version (with or without 'v' prefix):
# Option 1: Tag with 'v' prefix
git tag v0.2.0
# Option 2: Tag without prefix
git tag 0.2.0
# Push the tag
git push origin v0.2.0
Important: The tag version must match the version in pyproject.toml exactly (excluding the 'v' prefix if used).
5. Create GitHub Release
Go to the GitHub Releases page and click "Draft a new release":
For Pre-Release (Testing):
- Tag: Select the tag you just created (e.g.,
v0.2.0) - Release title:
v0.2.0(or your version) - Description: Copy from
CHANGELOG.mdor write release notes - ☑️ Set as a pre-release: Check this box
- Click "Publish release"
Pre-releases are not published to PyPI. Use them for testing before the final release.
For Final Release (Publishing to PyPI):
- Tag: Select the tag you just created (e.g.,
v0.2.0) - Release title:
v0.2.0(or your version) - Description: Copy from
CHANGELOG.mdor write release notes - ☐ Set as a pre-release: Leave this unchecked
- Click "Publish release"
The GitHub Actions workflow will:
- Verify the tag version matches
pyproject.toml - Build the package
- Check the package with
twine - Publish to PyPI (only for final releases, not pre-releases)
Workflow Summary
1. Update version in pyproject.toml
2. Update CHANGELOG.md
3. Commit and push changes
4. Create and push git tag
5. Create GitHub Release (pre-release or final)
└─> Pre-release: Testing only, not published to PyPI
└─> Final release: Automatically published to PyPI
Troubleshooting
- Version mismatch error: Ensure the tag version (without 'v' prefix) exactly matches
pyproject.tomlversion - Pre-release published: Pre-releases are intentionally skipped. Create a final release to publish to PyPI
- Workflow not triggered: Ensure the release is "Published" (not "Draft") and the tag exists
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
If you encounter any issues or have questions, please open an issue on GitHub.
Changelog
See CHANGELOG.md for a list of changes and version history.
Acknowledgments
- War Track Dashboard API team
- All contributors who help improve this library
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uruwat-0.2.0.tar.gz.
File metadata
- Download URL: uruwat-0.2.0.tar.gz
- Upload date:
- Size: 16.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dac101d91cf365aa4d9c2ce7c4b20e2646f507c44374c2f5cfdead90016152b8
|
|
| MD5 |
00534d524d1f0e39be6d4b6d2a0692d5
|
|
| BLAKE2b-256 |
d030a269b36d86dd3f9e2c2d5cf0e67bbcd885c1f3fc4807d2fe0ebf1dd2d9aa
|
Provenance
The following attestation bundles were made for uruwat-0.2.0.tar.gz:
Publisher:
publish.yml on WAT-Suite/uruwat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uruwat-0.2.0.tar.gz -
Subject digest:
dac101d91cf365aa4d9c2ce7c4b20e2646f507c44374c2f5cfdead90016152b8 - Sigstore transparency entry: 821195619
- Sigstore integration time:
-
Permalink:
WAT-Suite/uruwat@d9ddd20b3bc88b0a7117f117122adc1cb7e67188 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/WAT-Suite
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d9ddd20b3bc88b0a7117f117122adc1cb7e67188 -
Trigger Event:
release
-
Statement type:
File details
Details for the file uruwat-0.2.0-py3-none-any.whl.
File metadata
- Download URL: uruwat-0.2.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d2606a12bef3132ca65934b96658493e91702686e423dd778d170311efd51a4
|
|
| MD5 |
66a664fa6307a9a104c024e6ce1a3009
|
|
| BLAKE2b-256 |
1c40030369ae40ded0f93cc9420843cc9a55bbd6c975e06417e58aef1c42dbc2
|
Provenance
The following attestation bundles were made for uruwat-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on WAT-Suite/uruwat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uruwat-0.2.0-py3-none-any.whl -
Subject digest:
2d2606a12bef3132ca65934b96658493e91702686e423dd778d170311efd51a4 - Sigstore transparency entry: 821195620
- Sigstore integration time:
-
Permalink:
WAT-Suite/uruwat@d9ddd20b3bc88b0a7117f117122adc1cb7e67188 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/WAT-Suite
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d9ddd20b3bc88b0a7117f117122adc1cb7e67188 -
Trigger Event:
release
-
Statement type: