truesight-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ # Rust
2
+ /target/
3
+ **/*.rs.bk
4
+ *.pdb
5
+
6
+ # Node
7
+ node_modules/
8
+ dist/
9
+ build/
10
+ *.tsbuildinfo
11
+
12
+ # Environment
13
+ .env
14
+ .env.local
15
+ .env.*.local
16
+ !.env.example
17
+
18
+ # IDE
19
+ .idea/
20
+ .vscode/
21
+ *.swp
22
+ *.swo
23
+ *~
24
+
25
+ # OS
26
+ .DS_Store
27
+ Thumbs.db
28
+
29
+ # Gradle / KMM
30
+ .gradle/
31
+ sdks/kmm/build/
32
+ sdks/kmm/**/build/
33
+ sdks/kmm/local.properties
34
+ sdks/kmm/.kotlin/
35
+ **/*.klib
36
+
37
+ # Python SDK
38
+ sdks/python/.venv/
39
+ sdks/python/dist/
40
+ sdks/python/build/
41
+ **/*.egg-info/
42
+ sdks/python/.pytest_cache/
43
+ sdks/python/.mypy_cache/
44
+ sdks/python/.ruff_cache/
45
+ **/__pycache__/
46
+
47
+ # Database
48
+ *.db
49
+ *.sqlite
50
+
51
+ # Logs
52
+ *.log
53
+
54
+ # Docker volumes
55
+ clickhouse_data/
56
+ postgres_data/
57
+
58
+ # Lock files (pnpm is canonical, npm lock is SDK-local)
59
+ # sdks/web/package-lock.json is committed for npm publish
60
+
61
+ # Superpowers brainstorm artifacts
62
+ .superpowers/
63
+
64
+ # Claude Code
65
+ .claude/
66
+ .gstack/
@@ -0,0 +1,200 @@
1
+ Metadata-Version: 2.4
2
+ Name: truesight-sdk
3
+ Version: 0.1.0
4
+ Summary: Server-side Python SDK for TrueSight analytics ingestion
5
+ Project-URL: Homepage, https://github.com/komorebitech/cf-truesight
6
+ Project-URL: Source, https://github.com/komorebitech/cf-truesight/tree/master/sdks/python
7
+ Project-URL: Issues, https://github.com/komorebitech/cf-truesight/issues
8
+ Author-email: Cityflo Engineering <tech@cityflo.com>
9
+ License-Expression: MIT
10
+ Keywords: analytics,cityflo,event-tracking,truesight
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: requests>=2.28
24
+ Requires-Dist: urllib3>=2.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: mypy>=1.10; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: responses>=0.25; extra == 'dev'
29
+ Requires-Dist: ruff>=0.6; extra == 'dev'
30
+ Requires-Dist: types-requests; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # truesight-sdk
34
+
35
+ Server-side Python SDK for [TrueSight](https://github.com/komorebitech/cf-truesight) analytics ingestion.
36
+
37
+ 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.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install truesight-sdk
43
+ ```
44
+
45
+ Python 3.10+.
46
+
47
+ ## Quick Start
48
+
49
+ ### Single event
50
+
51
+ ```python
52
+ from truesight_sdk import TrueSightClient
53
+
54
+ client = TrueSightClient(
55
+ api_key="ts_server_live_...",
56
+ base_url="https://ingest.truesight.example.com",
57
+ )
58
+
59
+ client.track(
60
+ event_name="Purchased Lite Pack",
61
+ user_id=str(customer.pk),
62
+ properties={"plan_slug": "5-30-days", "amount": 1200},
63
+ )
64
+ ```
65
+
66
+ ### User profile update (identify)
67
+
68
+ ```python
69
+ client.identify(
70
+ user_id=str(customer.pk),
71
+ email=customer.email,
72
+ properties={
73
+ "home_locality": "Andheri",
74
+ "favourite_route": "M1",
75
+ "weekly_subscription_active": True,
76
+ },
77
+ )
78
+ ```
79
+
80
+ 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.
81
+
82
+ ### Batched ingestion (Airflow / cron / bulk syncs)
83
+
84
+ For workloads that emit many events in a tight loop, use `BatchingClient` to amortize HTTP overhead:
85
+
86
+ ```python
87
+ from truesight_sdk import BatchingClient, TrueSightClient
88
+
89
+ inner = TrueSightClient(api_key="ts_server_live_...", base_url="...")
90
+
91
+ with BatchingClient(inner, batch_size=100, flush_interval_seconds=5.0) as batcher:
92
+ for customer in qs.iterator():
93
+ batcher.identify(
94
+ user_id=str(customer.pk),
95
+ properties=build_profile(customer),
96
+ )
97
+ # Buffers drain on context exit.
98
+ ```
99
+
100
+ `BatchingClient` is thread-safe; multiple producer threads can call `track()` / `identify()` concurrently.
101
+
102
+ ### Reading events (admin queries)
103
+
104
+ The SDK also wraps TrueSight's admin event-query endpoint for read-path consumers:
105
+
106
+ ```python
107
+ from truesight_sdk import AdminQueryClient
108
+
109
+ reader = AdminQueryClient(admin_token="...", base_url="https://admin.truesight.example.com")
110
+
111
+ page = reader.fetch_events(
112
+ project_id="b219fb11-9a63-4843-8126-a3dc05b330a5",
113
+ event_name="Purchased Lite Pack",
114
+ from_=datetime(2026, 5, 1, tzinfo=timezone.utc),
115
+ to=datetime(2026, 5, 28, tzinfo=timezone.utc),
116
+ limit=500,
117
+ )
118
+ ```
119
+
120
+ ## API Keys
121
+
122
+ Server keys are scoped — they can only call `/v1/server/*` endpoints. Issue one via the CLI:
123
+
124
+ ```bash
125
+ truesight projects api-keys create --scope server --label "<your service>"
126
+ ```
127
+
128
+ The plaintext key is returned **once** at creation time. Store it in your secrets manager.
129
+
130
+ ## Error Handling
131
+
132
+ All errors subclass `TrueSightError`:
133
+
134
+ ```python
135
+ from truesight_sdk import (
136
+ AuthError, # 401 — bad / revoked key
137
+ Forbidden, # 403 — wrong scope for this endpoint
138
+ ValidationError, # 400/422 — payload rejected
139
+ RateLimited, # 429 — slow down (after SDK's own retry budget)
140
+ ServerError, # 5xx — TrueSight is degraded (after retries)
141
+ TrueSightError, # base class — catch this to handle any SDK error
142
+ )
143
+
144
+ try:
145
+ client.track("x", user_id="42")
146
+ except RateLimited:
147
+ schedule_retry(...)
148
+ except TrueSightError as exc:
149
+ log.exception("truesight ingest failed", request_id=exc.request_id)
150
+ ```
151
+
152
+ Every error carries `status_code`, `request_id` (when the server returned one), and `response_body` for log correlation.
153
+
154
+ ## Retry Idempotency
155
+
156
+ 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:
157
+
158
+ ```python
159
+ from truesight_sdk import TrackEvent
160
+ from uuid import uuid5, NAMESPACE_URL
161
+
162
+ stable_id = uuid5(NAMESPACE_URL, f"booking-confirmed:{booking.pk}")
163
+ client.track_batch([
164
+ TrackEvent(
165
+ event_name="Booking Confirmed",
166
+ user_id=str(booking.customer_id),
167
+ event_id=stable_id,
168
+ properties={"booking_id": booking.pk},
169
+ ),
170
+ ])
171
+ ```
172
+
173
+ The server dedups on `(project_id, event_id)` via ClickHouse's `ReplacingMergeTree`, so retries with the same id collapse to a single row.
174
+
175
+ ## Development
176
+
177
+ ```bash
178
+ # Install in dev mode
179
+ pip install -e ".[dev]"
180
+
181
+ # Run unit tests (no network)
182
+ pytest
183
+
184
+ # Run integration tests against a local TrueSight stack
185
+ export TRUESIGHT_BASE_URL=http://localhost:8080
186
+ export TRUESIGHT_API_KEY=ts_server_test_...
187
+ pytest -m integration
188
+
189
+ # Lint + typecheck
190
+ ruff check src/ tests/
191
+ mypy src/
192
+ ```
193
+
194
+ ## Versioning
195
+
196
+ Semver. `0.x` is pre-1.0 — the public API may shift between minor versions; pin the minor version until `1.0`.
197
+
198
+ ## License
199
+
200
+ MIT — see top-level repo.
@@ -0,0 +1,168 @@
1
+ # truesight-sdk
2
+
3
+ Server-side Python SDK for [TrueSight](https://github.com/komorebitech/cf-truesight) analytics ingestion.
4
+
5
+ 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.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install truesight-sdk
11
+ ```
12
+
13
+ Python 3.10+.
14
+
15
+ ## Quick Start
16
+
17
+ ### Single event
18
+
19
+ ```python
20
+ from truesight_sdk import TrueSightClient
21
+
22
+ client = TrueSightClient(
23
+ api_key="ts_server_live_...",
24
+ base_url="https://ingest.truesight.example.com",
25
+ )
26
+
27
+ client.track(
28
+ event_name="Purchased Lite Pack",
29
+ user_id=str(customer.pk),
30
+ properties={"plan_slug": "5-30-days", "amount": 1200},
31
+ )
32
+ ```
33
+
34
+ ### User profile update (identify)
35
+
36
+ ```python
37
+ client.identify(
38
+ user_id=str(customer.pk),
39
+ email=customer.email,
40
+ properties={
41
+ "home_locality": "Andheri",
42
+ "favourite_route": "M1",
43
+ "weekly_subscription_active": True,
44
+ },
45
+ )
46
+ ```
47
+
48
+ 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.
49
+
50
+ ### Batched ingestion (Airflow / cron / bulk syncs)
51
+
52
+ For workloads that emit many events in a tight loop, use `BatchingClient` to amortize HTTP overhead:
53
+
54
+ ```python
55
+ from truesight_sdk import BatchingClient, TrueSightClient
56
+
57
+ inner = TrueSightClient(api_key="ts_server_live_...", base_url="...")
58
+
59
+ with BatchingClient(inner, batch_size=100, flush_interval_seconds=5.0) as batcher:
60
+ for customer in qs.iterator():
61
+ batcher.identify(
62
+ user_id=str(customer.pk),
63
+ properties=build_profile(customer),
64
+ )
65
+ # Buffers drain on context exit.
66
+ ```
67
+
68
+ `BatchingClient` is thread-safe; multiple producer threads can call `track()` / `identify()` concurrently.
69
+
70
+ ### Reading events (admin queries)
71
+
72
+ The SDK also wraps TrueSight's admin event-query endpoint for read-path consumers:
73
+
74
+ ```python
75
+ from truesight_sdk import AdminQueryClient
76
+
77
+ reader = AdminQueryClient(admin_token="...", base_url="https://admin.truesight.example.com")
78
+
79
+ page = reader.fetch_events(
80
+ project_id="b219fb11-9a63-4843-8126-a3dc05b330a5",
81
+ event_name="Purchased Lite Pack",
82
+ from_=datetime(2026, 5, 1, tzinfo=timezone.utc),
83
+ to=datetime(2026, 5, 28, tzinfo=timezone.utc),
84
+ limit=500,
85
+ )
86
+ ```
87
+
88
+ ## API Keys
89
+
90
+ Server keys are scoped — they can only call `/v1/server/*` endpoints. Issue one via the CLI:
91
+
92
+ ```bash
93
+ truesight projects api-keys create --scope server --label "<your service>"
94
+ ```
95
+
96
+ The plaintext key is returned **once** at creation time. Store it in your secrets manager.
97
+
98
+ ## Error Handling
99
+
100
+ All errors subclass `TrueSightError`:
101
+
102
+ ```python
103
+ from truesight_sdk import (
104
+ AuthError, # 401 — bad / revoked key
105
+ Forbidden, # 403 — wrong scope for this endpoint
106
+ ValidationError, # 400/422 — payload rejected
107
+ RateLimited, # 429 — slow down (after SDK's own retry budget)
108
+ ServerError, # 5xx — TrueSight is degraded (after retries)
109
+ TrueSightError, # base class — catch this to handle any SDK error
110
+ )
111
+
112
+ try:
113
+ client.track("x", user_id="42")
114
+ except RateLimited:
115
+ schedule_retry(...)
116
+ except TrueSightError as exc:
117
+ log.exception("truesight ingest failed", request_id=exc.request_id)
118
+ ```
119
+
120
+ Every error carries `status_code`, `request_id` (when the server returned one), and `response_body` for log correlation.
121
+
122
+ ## Retry Idempotency
123
+
124
+ 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:
125
+
126
+ ```python
127
+ from truesight_sdk import TrackEvent
128
+ from uuid import uuid5, NAMESPACE_URL
129
+
130
+ stable_id = uuid5(NAMESPACE_URL, f"booking-confirmed:{booking.pk}")
131
+ client.track_batch([
132
+ TrackEvent(
133
+ event_name="Booking Confirmed",
134
+ user_id=str(booking.customer_id),
135
+ event_id=stable_id,
136
+ properties={"booking_id": booking.pk},
137
+ ),
138
+ ])
139
+ ```
140
+
141
+ The server dedups on `(project_id, event_id)` via ClickHouse's `ReplacingMergeTree`, so retries with the same id collapse to a single row.
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ # Install in dev mode
147
+ pip install -e ".[dev]"
148
+
149
+ # Run unit tests (no network)
150
+ pytest
151
+
152
+ # Run integration tests against a local TrueSight stack
153
+ export TRUESIGHT_BASE_URL=http://localhost:8080
154
+ export TRUESIGHT_API_KEY=ts_server_test_...
155
+ pytest -m integration
156
+
157
+ # Lint + typecheck
158
+ ruff check src/ tests/
159
+ mypy src/
160
+ ```
161
+
162
+ ## Versioning
163
+
164
+ Semver. `0.x` is pre-1.0 — the public API may shift between minor versions; pin the minor version until `1.0`.
165
+
166
+ ## License
167
+
168
+ MIT — see top-level repo.
@@ -0,0 +1,84 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "truesight-sdk"
7
+ description = "Server-side Python SDK for TrueSight analytics ingestion"
8
+ readme = "README.md"
9
+ license = "MIT"
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "Cityflo Engineering", email = "tech@cityflo.com" },
13
+ ]
14
+ keywords = ["analytics", "truesight", "event-tracking", "cityflo"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "requests>=2.28",
30
+ "urllib3>=2.0",
31
+ ]
32
+ dynamic = ["version"]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/komorebitech/cf-truesight"
36
+ Source = "https://github.com/komorebitech/cf-truesight/tree/master/sdks/python"
37
+ Issues = "https://github.com/komorebitech/cf-truesight/issues"
38
+
39
+ [project.optional-dependencies]
40
+ dev = [
41
+ "pytest>=8.0",
42
+ "responses>=0.25",
43
+ "ruff>=0.6",
44
+ "mypy>=1.10",
45
+ "types-requests",
46
+ ]
47
+
48
+ [tool.hatch.version]
49
+ path = "src/truesight_sdk/_version.py"
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["src/truesight_sdk"]
53
+
54
+ [tool.hatch.build.targets.sdist]
55
+ include = [
56
+ "/src",
57
+ "/tests",
58
+ "/README.md",
59
+ "/pyproject.toml",
60
+ ]
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ target-version = "py310"
65
+
66
+ [tool.ruff.lint]
67
+ select = ["E", "F", "I", "N", "UP", "B", "SIM", "RUF"]
68
+ ignore = ["E501"]
69
+
70
+ [tool.mypy]
71
+ python_version = "3.10"
72
+ strict = true
73
+ warn_unreachable = true
74
+ disallow_untyped_decorators = false
75
+
76
+ [[tool.mypy.overrides]]
77
+ module = ["responses.*"]
78
+ ignore_missing_imports = true
79
+
80
+ [tool.pytest.ini_options]
81
+ testpaths = ["tests"]
82
+ markers = [
83
+ "integration: integration test that requires a running TrueSight instance (skipped unless TRUESIGHT_BASE_URL is set)",
84
+ ]
@@ -0,0 +1,54 @@
1
+ """TrueSight Python SDK — server-side analytics ingestion.
2
+
3
+ This SDK targets server-to-server use cases (Django/Flask/FastAPI backends,
4
+ Airflow DAGs, management commands) that emit events on behalf of authenticated
5
+ users. It is intentionally sync; if you need an async wrapper, wire it up via
6
+ your own task queue (e.g. Celery).
7
+
8
+ Quick start:
9
+
10
+ from truesight_sdk import TrueSightClient
11
+
12
+ client = TrueSightClient(
13
+ api_key="ts_server_live_...",
14
+ base_url="https://ingest.truesight.example.com",
15
+ )
16
+ client.track(
17
+ event_name="Purchased Lite Pack",
18
+ user_id=str(customer.pk),
19
+ properties={"plan_slug": "5-30-days", "amount": 1200},
20
+ )
21
+
22
+ For bulk-sync workloads (Airflow DAGs, nightly profile syncs), use
23
+ ``BatchingClient`` to amortize HTTP overhead across many events.
24
+ """
25
+
26
+ from truesight_sdk._version import __version__
27
+ from truesight_sdk.batching import BatchingClient
28
+ from truesight_sdk.client import TrueSightClient
29
+ from truesight_sdk.errors import (
30
+ AuthError,
31
+ Forbidden,
32
+ RateLimited,
33
+ ServerError,
34
+ TrueSightError,
35
+ ValidationError,
36
+ )
37
+ from truesight_sdk.models import EventType, IdentifyEvent, TrackEvent
38
+ from truesight_sdk.query import AdminQueryClient
39
+
40
+ __all__ = [
41
+ "AdminQueryClient",
42
+ "AuthError",
43
+ "BatchingClient",
44
+ "EventType",
45
+ "Forbidden",
46
+ "IdentifyEvent",
47
+ "RateLimited",
48
+ "ServerError",
49
+ "TrackEvent",
50
+ "TrueSightClient",
51
+ "TrueSightError",
52
+ "ValidationError",
53
+ "__version__",
54
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"