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
Metadata
Release files for uruwat 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| uruwat-0.2.0.tar.gz | 16.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| uruwat-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 29.3 kB
Release files / uruwat-0.2.0.tar.gz
| Download URL | uruwat-0.2.0.tar.gz |
|---|---|
| Size | 16.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dac101d91cf365aa4d9c2ce7c4b20e2646f507c44374c2f5cfdead90016152b8
|
|
BLAKE2b-256 checksum How to use checksums |
d030a269b36d86dd3f9e2c2d5cf0e67bbcd885c1f3fc4807d2fe0ebf1dd2d9aa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 14, 2026.
Transparency logRelease files / uruwat-0.2.0-py3-none-any.whl
| Download URL | uruwat-0.2.0-py3-none-any.whl |
|---|---|
| Size | 13.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2d2606a12bef3132ca65934b96658493e91702686e423dd778d170311efd51a4
|
|
BLAKE2b-256 checksum How to use checksums |
1c40030369ae40ded0f93cc9420843cc9a55bbd6c975e06417e58aef1c42dbc2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 14, 2026.
Transparency log