Skip to main content

uruwat

PyPI version PyPI downloads Python versions License: MIT CI codecov Code style: black Ruff mypy GitHub stars

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 filter
  • date_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 filter
  • types (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 filter
  • status (list[Status], optional): List of statuses to filter
  • date_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 filter
  • systems (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.ALL
  • Country.UKRAINE
  • Country.RUSSIA

EquipmentType

  • EquipmentType.TANKS
  • EquipmentType.AIRCRAFT
  • EquipmentType.HELICOPTERS
  • ... (see full list in code)

Status

  • Status.DESTROYED
  • Status.CAPTURED
  • Status.ABANDONED
  • Status.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 errors
  • WarTrackAuthenticationError: Raised on 401 Unauthorized
  • WarTrackForbiddenError: Raised on 403 Forbidden
  • WarTrackNotFoundError: Raised on 404 Not Found
  • WarTrackRateLimitError: Raised on 429 Too Many Requests
  • WarTrackServerError: 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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass and code is formatted (uv run black . && uv run ruff check . && uv run pytest)
  6. Commit your changes (following Conventional Commits)
  7. Push to the branch (git push origin feature/amazing-feature)
  8. 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 feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting, etc.)
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: 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.md or 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.md or write release notes
  • ☐ Set as a pre-release: Leave this unchecked
  • Click "Publish release"

The GitHub Actions workflow will:

  1. Verify the tag version matches pyproject.toml
  2. Build the package
  3. Check the package with twine
  4. 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.toml version
  • 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)

Source distribution for uruwat 0.2.0
File Size Uploaded
uruwat-0.2.0.tar.gz 16.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for uruwat 0.2.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page