cle-engine 0.1.0
Python SDK for the CLE Engine API - Continuing Legal Education deadline computation
Requires Python: >=3.8
Keywords: cle, legal, continuing education, api, sdk

# CLE Engine Python SDK

A Python client library for the [CLE Engine API](https://cle-engine.com), providing easy access to continuing legal education (CLE) deadline computation across all US jurisdictions.

## Installation

```bash
pip install cle-engine
```

## Quick Start

```python
from cle_engine import CLEEngineClient

# Initialize the client
client = CLEEngineClient(api_key="your-api-key")

# Compute CLE due date for a California attorney
result = client.compute_due_date(
    jurisdiction="CA",
    admission_date="2020-01-15"
)

print(f"Due date: {result.due_date}")
print(f"Credits required: {result.credits_required}")
print(f"Cycle: {result.cycle_start} to {result.cycle_end}")
```

## Configuration

### API Key

You can provide your API key in two ways:

1. **Direct initialization:**
   ```python
   client = CLEEngineClient(api_key="your-api-key")
   ```

2. **Environment variable:**
   ```bash
   export CLE_ENGINE_API_KEY="your-api-key"
   ```
   ```python
   client = CLEEngineClient()  # Reads from environment
   ```

### Custom Base URL

For testing or self-hosted instances:

```python
client = CLEEngineClient(
    api_key="your-api-key",
    base_url="https://your-instance.example.com"
)
```

Or via environment variable:
```bash
export CLE_ENGINE_BASE_URL="https://your-instance.example.com"
```

## Usage Examples

### Computing Due Dates

Basic computation:

```python
from cle_engine import CLEEngineClient
from datetime import date

client = CLEEngineClient(api_key="your-api-key")

# Using string dates
result = client.compute_due_date(
    jurisdiction="CA",
    admission_date="2020-01-15"
)

# Using date objects
result = client.compute_due_date(
    jurisdiction="NY",
    admission_date=date(2018, 5, 1),
    last_name="Smith"  # Used for surname-based reporting groups
)
```

With all optional parameters:

```python
result = client.compute_due_date(
    jurisdiction="TX",
    profession="lawyer",
    last_name="Johnson",
    birth_date="1985-03-20",
    admission_date="2015-07-01",
    reporting_category="Group1",
    reporting_period_end="2025-03-31",
    renewal_year=2025
)

# Access response fields
print(f"Due date: {result.due_date}")
print(f"CLE required: {result.cle_required}")
print(f"Credits required: {result.credits_required}")
print(f"Reporting group: {result.reporting_group}")
print(f"Missing fields: {result.missing_fields}")
print(f"Notes: {result.notes}")
```

### Health Check

```python
client = CLEEngineClient(api_key="your-api-key")
health = client.health_check()
print(f"API status: {health.status}")  # "ok"
```

### Getting Jurisdictions

```python
client = CLEEngineClient(api_key="your-api-key")
jurisdictions = client.get_jurisdictions()

for j in jurisdictions:
    print(f"{j.code}: {j.name}")
    print(f"  CLE required: {j.cle_required}")
    if j.cle_required:
        print(f"  Credits per cycle: {j.credits_per_cycle}")
        print(f"  Cycle length: {j.cycle_years} years")
```

### Using Context Manager

```python
with CLEEngineClient(api_key="your-api-key") as client:
    result = client.compute_due_date(jurisdiction="CA")
    print(result.due_date)
# Connection is automatically closed
```

## Error Handling

The SDK provides specific exception classes for different error types:

```python
from cle_engine import CLEEngineClient
from cle_engine.exceptions import (
    CLEEngineError,
    AuthenticationError,
    RateLimitError,
    ValidationError,
    ServerError,
    NetworkError,
)

client = CLEEngineClient(api_key="your-api-key")

try:
    result = client.compute_due_date(jurisdiction="INVALID")
except AuthenticationError as e:
    print(f"Authentication failed: {e}")
    # Invalid or expired API key
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except ValidationError as e:
    print(f"Invalid request: {e}")
    print(f"Details: {e.response_body}")
except ServerError as e:
    print(f"Server error: {e}")
except NetworkError as e:
    print(f"Network error: {e}")
except CLEEngineError as e:
    # Catch-all for any API error
    print(f"API error [{e.status_code}]: {e.message}")
```

### Exception Hierarchy

All exceptions inherit from `CLEEngineError`:

- `AuthenticationError` - Invalid or missing API key (401/403)
- `RateLimitError` - Rate limit exceeded (429)
- `ValidationError` - Invalid request parameters (400/422)
- `ServerError` - Server-side errors (5xx)
- `NetworkError` - Connection/timeout issues

## Response Models

### ComputeResponse

| Field | Type | Description |
|-------|------|-------------|
| `due_date` | `date \| None` | The CLE compliance deadline |
| `cycle_start` | `date \| None` | Start of the reporting cycle |
| `cycle_end` | `date \| None` | End of the reporting cycle |
| `credits_required` | `int \| None` | Total credits required |
| `reporting_group` | `str \| None` | Assigned reporting group |
| `cle_required` | `bool \| None` | Whether CLE is mandatory |
| `required_fields` | `list[str]` | Fields needed for computation |
| `missing_fields` | `list[str]` | Required fields not provided |
| `citations` | `list[Citation]` | Source documentation |
| `notes` | `str \| None` | Additional information |

### Citation

| Field | Type | Description |
|-------|------|-------------|
| `source_id` | `str` | Identifier for the source |
| `excerpt_id` | `str \| None` | Specific excerpt reference |
| `url` | `str \| None` | Link to source documentation |

## Development

### Running Tests

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=cle_engine --cov-report=term-missing

# Type checking
mypy cle_engine

# Linting
ruff check cle_engine
```

### Building

```bash
pip install build
python -m build
```

## Requirements

- Python 3.8+
- requests >= 2.25.0
- pydantic >= 2.0.0

## License

MIT License - see LICENSE file for details.

## Support

- **Documentation:** https://docs.cle-engine.com
- **Issues:** https://github.com/cle-engine/cle-engine/issues
- **Email:** support@cle-engine.com