Metadata-Version: 2.4
Name: truesight-sdk
Version: 0.1.0a1
Summary: Server-side Python SDK for TrueSight analytics ingestion
Project-URL: Homepage, https://github.com/komorebitech/cf-truesight
Project-URL: Source, https://github.com/komorebitech/cf-truesight/tree/master/sdks/python
Project-URL: Issues, https://github.com/komorebitech/cf-truesight/issues
Author-email: Cityflo Engineering <tech@cityflo.com>
License-Expression: MIT
Keywords: analytics,cityflo,event-tracking,truesight
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: requests>=2.28
Requires-Dist: urllib3>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: responses>=0.25; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: types-requests; extra == 'dev'
Description-Content-Type: text/markdown

# truesight-sdk

Server-side Python SDK for [TrueSight](https://github.com/komorebitech/cf-truesight) analytics ingestion.

Designed for backends that emit events on behalf of authenticated users (Django, Flask, FastAPI, Airflow DAGs, management commands). Sync by design — if you need async, wrap calls in your own task queue.

## Installation

```bash
pip install truesight-sdk
```

Python 3.10+.

## Quick Start

### Single event

```python
from truesight_sdk import TrueSightClient

client = TrueSightClient(
    api_key="ts_server_live_...",
    base_url="https://ingest.truesight.example.com",
)

client.track(
    event_name="Purchased Lite Pack",
    user_id=str(customer.pk),
    properties={"plan_slug": "5-30-days", "amount": 1200},
)
```

### User profile update (identify)

```python
client.identify(
    user_id=str(customer.pk),
    email=customer.email,
    properties={
        "home_locality": "Andheri",
        "favourite_route": "M1",
        "weekly_subscription_active": True,
    },
)
```

This upserts the user's row in `truesight.user_profiles` (latest-write-wins merge of the `properties` blob) and also appends a `$identify` event to the stream for history.

### Batched ingestion (Airflow / cron / bulk syncs)

For workloads that emit many events in a tight loop, use `BatchingClient` to amortize HTTP overhead:

```python
from truesight_sdk import BatchingClient, TrueSightClient

inner = TrueSightClient(api_key="ts_server_live_...", base_url="...")

with BatchingClient(inner, batch_size=100, flush_interval_seconds=5.0) as batcher:
    for customer in qs.iterator():
        batcher.identify(
            user_id=str(customer.pk),
            properties=build_profile(customer),
        )
# Buffers drain on context exit.
```

`BatchingClient` is thread-safe; multiple producer threads can call `track()` / `identify()` concurrently.

### Reading events (admin queries)

The SDK also wraps TrueSight's admin event-query endpoint for read-path consumers:

```python
from truesight_sdk import AdminQueryClient

reader = AdminQueryClient(admin_token="...", base_url="https://admin.truesight.example.com")

page = reader.fetch_events(
    project_id="b219fb11-9a63-4843-8126-a3dc05b330a5",
    event_name="Purchased Lite Pack",
    from_=datetime(2026, 5, 1, tzinfo=timezone.utc),
    to=datetime(2026, 5, 28, tzinfo=timezone.utc),
    limit=500,
)
```

## API Keys

Server keys are scoped — they can only call `/v1/server/*` endpoints. Issue one via the CLI:

```bash
truesight projects api-keys create --scope server --label "<your service>"
```

The plaintext key is returned **once** at creation time. Store it in your secrets manager.

## Error Handling

All errors subclass `TrueSightError`:

```python
from truesight_sdk import (
    AuthError,        # 401 — bad / revoked key
    Forbidden,        # 403 — wrong scope for this endpoint
    ValidationError,  # 400/422 — payload rejected
    RateLimited,      # 429 — slow down (after SDK's own retry budget)
    ServerError,      # 5xx — TrueSight is degraded (after retries)
    TrueSightError,   # base class — catch this to handle any SDK error
)

try:
    client.track("x", user_id="42")
except RateLimited:
    schedule_retry(...)
except TrueSightError as exc:
    log.exception("truesight ingest failed", request_id=exc.request_id)
```

Every error carries `status_code`, `request_id` (when the server returned one), and `response_body` for log correlation.

## Retry Idempotency

The SDK auto-generates a fresh `event_id` (UUIDv4) for every event when one isn't supplied. If you need retry idempotency (e.g. inside a Celery task that may run twice), pass an explicit `event_id` stable across retries:

```python
from truesight_sdk import TrackEvent
from uuid import uuid5, NAMESPACE_URL

stable_id = uuid5(NAMESPACE_URL, f"booking-confirmed:{booking.pk}")
client.track_batch([
    TrackEvent(
        event_name="Booking Confirmed",
        user_id=str(booking.customer_id),
        event_id=stable_id,
        properties={"booking_id": booking.pk},
    ),
])
```

The server dedups on `(project_id, event_id)` via ClickHouse's `ReplacingMergeTree`, so retries with the same id collapse to a single row.

## Development

```bash
# Install in dev mode
pip install -e ".[dev]"

# Run unit tests (no network)
pytest

# Run integration tests against a local TrueSight stack
export TRUESIGHT_BASE_URL=http://localhost:8080
export TRUESIGHT_API_KEY=ts_server_test_...
pytest -m integration

# Lint + typecheck
ruff check src/ tests/
mypy src/
```

## Versioning

Semver. `0.x` is alpha — the public API may shift; pin the patch version until `1.0`.

## License

MIT — see top-level repo.
