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.
- ✅ 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+
pip install uruwatOr using uv:
uv add uruwatOr using poetry:
poetry add uruwatfrom 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()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
)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 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 distinct equipment types.
Returns: list[dict[str, str]]
Example:
types = client.get_equipment_types()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 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 distinct system types.
Returns: list[dict[str, str]]
Example:
types = client.get_system_types()Trigger import of equipment data from scraper.
Returns: dict[str, str]
Trigger import of all equipment totals from scraper.
Returns: dict[str, str]
Trigger import of system data from scraper.
Returns: dict[str, str]
Trigger import of all system totals from scraper.
Returns: dict[str, str]
Trigger import of all data from scraper.
Returns: dict[str, str]
Check API health status.
Returns: dict[str, str]
class Equipment:
id: int
country: str
type: str
destroyed: int
abandoned: int
captured: int
damaged: int
total: int
date: strclass AllEquipment:
id: int
country: str
type: str
destroyed: int
abandoned: int
captured: int
damaged: int
total: intclass System:
id: int
country: str
origin: str
system: str
status: str
url: str
date: strclass AllSystem:
id: int
country: str
system: str
destroyed: int
abandoned: int
captured: int
damaged: int
total: intCountry.ALLCountry.UKRAINECountry.RUSSIA
EquipmentType.TANKSEquipmentType.AIRCRAFTEquipmentType.HELICOPTERS- ... (see full list in code)
Status.DESTROYEDStatus.CAPTUREDStatus.ABANDONEDStatus.DAMAGED
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}")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
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# 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# 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# Format code
uv run black .
# Lint code
uv run ruff check .
# Type checking
uv run mypy uruwatYou 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 pytestContributions are welcome! Please feel free to submit a Pull Request.
- 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
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
- 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
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.
Update the version in pyproject.toml:
[project]
version = "0.2.0" # Update to your new versionFollow Semantic Versioning:
- MAJOR (1.0.0): Breaking changes
- MINOR (0.1.0): New features, backwards compatible
- PATCH (0.0.1): Bug fixes, backwards compatible
Update CHANGELOG.md with the changes for this version.
git add pyproject.toml CHANGELOG.md
git commit -m "chore: bump version to 0.2.0"
git push origin mainCreate 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.0Important: The tag version must match the version in pyproject.toml exactly (excluding the 'v' prefix if used).
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)
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
- 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
This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have questions, please open an issue on GitHub.
See CHANGELOG.md for a list of changes and version history.
- War Track Dashboard API team
- All contributors who help improve this library