Metadata-Version: 2.4
Name: timebutler_client
Version: 0.0.4
Summary: Async Python client for the Timebutler API
Project-URL: Changelog, https://github.com/Hochfrequenz/timebutler_client.py/releases
Project-URL: Homepage, https://github.com/Hochfrequenz/timebutler_client.py
Author-email: Hochfrequenz Unternehmensberatung GmbH <info+github@hochfrequenz.de>
License: MIT
Keywords: aiohttp,api,async,client,timebutler
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.11
Requires-Dist: aiohttp>=3.10
Requires-Dist: pydantic>=2.5
Provides-Extra: coverage
Requires-Dist: coverage==7.13.5; extra == 'coverage'
Provides-Extra: dev
Requires-Dist: pip-tools; extra == 'dev'
Provides-Extra: formatting
Requires-Dist: black==26.3.1; extra == 'formatting'
Requires-Dist: isort==8.0.1; extra == 'formatting'
Provides-Extra: linting
Requires-Dist: pylint==4.0.5; extra == 'linting'
Provides-Extra: packaging
Requires-Dist: build==1.4.4; extra == 'packaging'
Requires-Dist: twine==6.2.0; extra == 'packaging'
Provides-Extra: spell-check
Requires-Dist: codespell==2.4.2; extra == 'spell-check'
Provides-Extra: tests
Requires-Dist: aioresponses==0.7.8; extra == 'tests'
Requires-Dist: pytest-asyncio==1.3.0; extra == 'tests'
Requires-Dist: pytest==9.0.3; extra == 'tests'
Provides-Extra: type-check
Requires-Dist: mypy==1.20.2; extra == 'type-check'
Description-Content-Type: text/markdown

# timebutler_client

Async Python client for the [Timebutler](https://timebutler.com) API ([official docs](https://app.timebutler.com/do?ha=api&ac=10)).

> [!IMPORTANT]
> This is NOT an official client by Timebutler GmbH, just a community project.

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
![Python Versions (officially) supported](https://img.shields.io/pypi/pyversions/timebutler-client.svg)
![Pypi status badge](https://img.shields.io/pypi/v/timebutler-client)
![Unittests status badge](https://github.com/Hochfrequenz/timebutler_client.py/workflows/Unittests/badge.svg)
![Coverage status badge](https://github.com/Hochfrequenz/timebutler_client.py/workflows/Coverage/badge.svg)
![Linting status badge](https://github.com/Hochfrequenz/timebutler_client.py/workflows/Linting/badge.svg)
![Formatting status badge](https://github.com/Hochfrequenz/timebutler_client.py/workflows/Formatting/badge.svg)

## Installation

```bash
pip install timebutler-client
```

## Usage

```python
from timebutler_client import TimebutlerClient

# Create client with your API key
client = TimebutlerClient(api_key="your-api-key")

# Fetch absences for a specific year
absences = await client.get_absences(year=2026)

for absence in absences:
    print(f"{absence.employee_number}: {absence.from_date} - {absence.to_date} ({absence.absence_type})")
```

## Features

> [!NOTE]
> We only implemented a subset of the Timebutler API endpoints, because the API is not very convenient to develop against (no OpenAPI, no sandbox or test system, only admin API keys).

- Async HTTP client using `aiohttp`
- Typed responses using Pydantic models
- Strict date parsing (European `dd/mm/yyyy` format)
- Employee number handling with leading zeros preserved

### Supported Endpoints

| Method | Description |
|--------|-------------|
| `get_absences(year)` | Fetch absences for a given year |
| `get_projects()` | Fetch all projects |
| `get_services()` | Fetch all services |
| `get_users()` | Fetch all users |
| `get_workdays()` | Fetch workday schedules for all users (see note below) |
| `get_worktime(year?, month?, user_id?)` | Fetch worktime entries with optional filters |

> [!NOTE]
> `get_workdays()` returns a `WorkdaysResult` with two named fields: `schedules` and `invalid_employees`.
> `invalid_employees` contains users whose `employee_number` field in Timebutler is empty or non-numeric.
> This is expected — it occurs for DC users on test systems and for new employees who are not yet fully
> set up in production. Because employee numbers are mandatory for downstream processing, these users are
> filtered out here rather than propagating incomplete data further along the pipeline.

### Example: Tracking Time by Project

```python
from timebutler_client import TimebutlerClient

client = TimebutlerClient(api_key="your-api-key")

# Get all projects and worktime entries
projects = await client.get_projects()
worktime = await client.get_worktime(year=2026, month=1)

# Build a project name lookup
project_names = {p.id: p.name_stripped for p in projects}

# Sum hours by project
for entry in worktime:
    if entry.has_project:
        project_name = project_names.get(entry.project_id, "Unknown")
        print(f"{entry.date}: {entry.duration} on {project_name}")
```

## Development

This project is based on the [Hochfrequenz Python Template Repository](https://github.com/Hochfrequenz/python_template_repository).
Refer to that repository for detailed setup instructions including tox configuration and IDE setup.

### Quick Start

```bash
# Create dev environment
tox -e dev

# Run tests
tox -e tests

# Run linting
tox -e linting

# Run type checking
tox -e type_check
```

## License

MIT
