Skip to main content

Python client wrapper for the War Track Dashboard API

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uruwat-0.2.0.tar.gz (16.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uruwat-0.2.0-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

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

Hashes for uruwat-0.2.0.tar.gz
Algorithm Hash digest
SHA256 dac101d91cf365aa4d9c2ce7c4b20e2646f507c44374c2f5cfdead90016152b8
MD5 00534d524d1f0e39be6d4b6d2a0692d5
BLAKE2b-256 d030a269b36d86dd3f9e2c2d5cf0e67bbcd885c1f3fc4807d2fe0ebf1dd2d9aa

See more details on using hashes here.

Provenance

The following attestation bundles were made for uruwat-0.2.0.tar.gz:

Publisher: publish.yml on WAT-Suite/uruwat

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for uruwat-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2d2606a12bef3132ca65934b96658493e91702686e423dd778d170311efd51a4
MD5 66a664fa6307a9a104c024e6ce1a3009
BLAKE2b-256 1c40030369ae40ded0f93cc9420843cc9a55bbd6c975e06417e58aef1c42dbc2

See more details on using hashes here.

Provenance

The following attestation bundles were made for uruwat-0.2.0-py3-none-any.whl:

Publisher: publish.yml on WAT-Suite/uruwat

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page