trodo-python 1.0.0__py3-none-any.whl → 1.2.0__py3-none-any.whl

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.
trodo/user_context.py CHANGED
@@ -1,224 +1,224 @@
1
- """UserContext — user-bound proxy for all Trodo SDK operations."""
2
-
3
- from __future__ import annotations
4
-
5
- import traceback
6
- from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
7
-
8
- from .managers.people_manager import PeopleManager
9
- from .managers.group_manager import GroupManager, GroupProfile
10
- from .types import (
11
- ApiResult,
12
- IdentifyResult,
13
- ResetResult,
14
- WalletAddressResult,
15
- EventPayload,
16
- )
17
- from .session.server_session import now_iso
18
-
19
- if TYPE_CHECKING:
20
- from .api.http_client import HttpClient
21
- from .session.session_manager import SessionManager
22
- from .queue.event_queue import EventQueue
23
- from .queue.batch_flusher import BatchFlusher
24
- from .auto.auto_event_manager import AutoEventManager
25
- from .types import ServerSession
26
-
27
-
28
- class UserContext:
29
- def __init__(
30
- self,
31
- distinct_id: str,
32
- site_id: str,
33
- http_client: "HttpClient",
34
- session_manager: "SessionManager",
35
- event_queue: Optional["EventQueue"],
36
- batch_flusher: Optional["BatchFlusher"],
37
- auto_event_manager: "AutoEventManager",
38
- session_id: Optional[str] = None,
39
- ) -> None:
40
- self._distinct_id = distinct_id
41
- self._site_id = site_id
42
- self._http = http_client
43
- self._session_manager = session_manager
44
- self._event_queue = event_queue
45
- self._batch_flusher = batch_flusher
46
- self._auto_event_manager = auto_event_manager
47
- self._session_id_override = session_id
48
- self._session: Optional["ServerSession"] = None
49
-
50
- # Eagerly initialise session
51
- self._session = self._session_manager.get_or_create(
52
- distinct_id, site_id, session_id
53
- )
54
-
55
- self.people = PeopleManager(
56
- http_client, site_id, lambda: self._get_distinct_id()
57
- )
58
- self._group_manager = GroupManager(
59
- http_client, site_id, lambda: self._get_distinct_id()
60
- )
61
-
62
- def _get_distinct_id(self) -> str:
63
- if self._session:
64
- return self._session.distinct_id
65
- return self._distinct_id
66
-
67
- def _get_session(self) -> "ServerSession":
68
- if not self._session:
69
- self._session = self._session_manager.get_or_create(
70
- self._distinct_id, self._site_id, self._session_id_override
71
- )
72
- return self._session
73
-
74
- def _send_event(
75
- self,
76
- event_name: str,
77
- properties: Optional[Dict[str, Any]] = None,
78
- category: str = "custom",
79
- ) -> None:
80
- session = self._get_session()
81
- self._session_manager.ensure_confirmed(session, self._http)
82
-
83
- event = EventPayload(
84
- event_type="custom",
85
- event_name=event_name,
86
- event_category=category,
87
- session_id=session.session_id,
88
- user_id=session.distinct_id,
89
- custom_properties=properties or {},
90
- )
91
-
92
- if self._event_queue and self._batch_flusher:
93
- should_flush = self._event_queue.enqueue(event)
94
- if should_flush:
95
- self._batch_flusher.flush()
96
- else:
97
- self._http.post_event(event)
98
-
99
- # --------------------------------------------------------------------------
100
- # Event Tracking
101
- # --------------------------------------------------------------------------
102
-
103
- def track(
104
- self,
105
- event_name: str,
106
- properties: Optional[Dict[str, Any]] = None,
107
- category: str = "custom",
108
- ) -> None:
109
- self._send_event(event_name, properties, category)
110
-
111
- def track_event(
112
- self,
113
- event_name: str,
114
- properties: Optional[Dict[str, Any]] = None,
115
- category: str = "custom",
116
- ) -> None:
117
- """Alias for track() — matches frontend SDK naming."""
118
- self._send_event(event_name, properties, category)
119
-
120
- # --------------------------------------------------------------------------
121
- # Identity
122
- # --------------------------------------------------------------------------
123
-
124
- def identify(self, identify_id: str) -> IdentifyResult:
125
- session = self._get_session()
126
- self._session_manager.ensure_confirmed(session, self._http)
127
-
128
- result = self._http.post_identify({
129
- "siteId": self._site_id,
130
- "sessionId": session.session_id,
131
- "userId": session.distinct_id,
132
- "distinctId": session.distinct_id,
133
- "identifyId": identify_id,
134
- })
135
-
136
- new_distinct_id = result.get("newDistinctId") or result.get("distinctId")
137
- if new_distinct_id and new_distinct_id != session.distinct_id:
138
- session.distinct_id = new_distinct_id
139
-
140
- return result
141
-
142
- def wallet_address(self, wallet_addr: str) -> WalletAddressResult:
143
- session = self._get_session()
144
- self._session_manager.ensure_confirmed(session, self._http)
145
-
146
- return self._http.post_wallet_address({
147
- "siteId": self._site_id,
148
- "sessionId": session.session_id,
149
- "userId": session.distinct_id,
150
- "distinctId": session.distinct_id,
151
- "walletAddress": wallet_addr,
152
- })
153
-
154
- def reset(self) -> ResetResult:
155
- session = self._get_session()
156
- result = self._http.post_reset({
157
- "siteId": self._site_id,
158
- "sessionId": session.session_id,
159
- "userId": session.distinct_id,
160
- "distinctId": session.distinct_id,
161
- })
162
- self._session_manager.invalidate(self._distinct_id)
163
- self._session = None
164
- return result
165
-
166
- # --------------------------------------------------------------------------
167
- # People — accessible via user.people.set(...) etc.
168
- # --------------------------------------------------------------------------
169
-
170
- # --------------------------------------------------------------------------
171
- # Groups
172
- # --------------------------------------------------------------------------
173
-
174
- def set_group(self, group_key: str, group_id: Union[str, List[str]]) -> ApiResult:
175
- self._get_session()
176
- return self._group_manager.set_group(group_key, group_id)
177
-
178
- def add_group(self, group_key: str, group_id: str) -> ApiResult:
179
- self._get_session()
180
- return self._group_manager.add_group(group_key, group_id)
181
-
182
- def remove_group(self, group_key: str, group_id: str) -> ApiResult:
183
- self._get_session()
184
- return self._group_manager.remove_group(group_key, group_id)
185
-
186
- def get_group(self, group_key: str, group_id: str) -> GroupProfile:
187
- return self._group_manager.get_group(group_key, group_id)
188
-
189
- # --------------------------------------------------------------------------
190
- # Error capture
191
- # --------------------------------------------------------------------------
192
-
193
- def capture_error(
194
- self,
195
- error: Exception,
196
- severity: str = "error",
197
- ) -> None:
198
- session = self._get_session()
199
- self._auto_event_manager.track_error(
200
- error,
201
- error_type=type(error).__name__,
202
- severity=severity,
203
- distinct_id=session.distinct_id,
204
- )
205
-
206
- # --------------------------------------------------------------------------
207
- # Auto events (per-context toggle)
208
- # --------------------------------------------------------------------------
209
-
210
- def enable_auto_events(self) -> None:
211
- self._auto_event_manager.enable()
212
-
213
- def disable_auto_events(self) -> None:
214
- self._auto_event_manager.disable()
215
-
216
- # --------------------------------------------------------------------------
217
- # Session info
218
- # --------------------------------------------------------------------------
219
-
220
- def get_session_id(self) -> Optional[str]:
221
- return self._session.session_id if self._session else None
222
-
223
- def get_current_distinct_id(self) -> str:
224
- return self._get_distinct_id()
1
+ """UserContext — user-bound proxy for all Trodo SDK operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import traceback
6
+ from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
7
+
8
+ from .managers.people_manager import PeopleManager
9
+ from .managers.group_manager import GroupManager, GroupProfile
10
+ from .types import (
11
+ ApiResult,
12
+ IdentifyResult,
13
+ ResetResult,
14
+ WalletAddressResult,
15
+ EventPayload,
16
+ )
17
+ from .session.server_session import now_iso
18
+
19
+ if TYPE_CHECKING:
20
+ from .api.http_client import HttpClient
21
+ from .session.session_manager import SessionManager
22
+ from .queue.event_queue import EventQueue
23
+ from .queue.batch_flusher import BatchFlusher
24
+ from .auto.auto_event_manager import AutoEventManager
25
+ from .types import ServerSession
26
+
27
+
28
+ class UserContext:
29
+ def __init__(
30
+ self,
31
+ distinct_id: str,
32
+ site_id: str,
33
+ http_client: "HttpClient",
34
+ session_manager: "SessionManager",
35
+ event_queue: Optional["EventQueue"],
36
+ batch_flusher: Optional["BatchFlusher"],
37
+ auto_event_manager: "AutoEventManager",
38
+ session_id: Optional[str] = None,
39
+ ) -> None:
40
+ self._distinct_id = distinct_id
41
+ self._site_id = site_id
42
+ self._http = http_client
43
+ self._session_manager = session_manager
44
+ self._event_queue = event_queue
45
+ self._batch_flusher = batch_flusher
46
+ self._auto_event_manager = auto_event_manager
47
+ self._session_id_override = session_id
48
+ self._session: Optional["ServerSession"] = None
49
+
50
+ # Eagerly initialise session
51
+ self._session = self._session_manager.get_or_create(
52
+ distinct_id, site_id, session_id
53
+ )
54
+
55
+ self.people = PeopleManager(
56
+ http_client, site_id, lambda: self._get_distinct_id()
57
+ )
58
+ self._group_manager = GroupManager(
59
+ http_client, site_id, lambda: self._get_distinct_id()
60
+ )
61
+
62
+ def _get_distinct_id(self) -> str:
63
+ if self._session:
64
+ return self._session.distinct_id
65
+ return self._distinct_id
66
+
67
+ def _get_session(self) -> "ServerSession":
68
+ if not self._session:
69
+ self._session = self._session_manager.get_or_create(
70
+ self._distinct_id, self._site_id, self._session_id_override
71
+ )
72
+ return self._session
73
+
74
+ def _send_event(
75
+ self,
76
+ event_name: str,
77
+ properties: Optional[Dict[str, Any]] = None,
78
+ category: str = "custom",
79
+ ) -> None:
80
+ session = self._get_session()
81
+ self._session_manager.ensure_confirmed(session, self._http)
82
+
83
+ event = EventPayload(
84
+ event_type="custom",
85
+ event_name=event_name,
86
+ event_category=category,
87
+ session_id=session.session_id,
88
+ user_id=session.distinct_id,
89
+ custom_properties=properties or {},
90
+ )
91
+
92
+ if self._event_queue and self._batch_flusher:
93
+ should_flush = self._event_queue.enqueue(event)
94
+ if should_flush:
95
+ self._batch_flusher.flush()
96
+ else:
97
+ self._http.post_event(event)
98
+
99
+ # --------------------------------------------------------------------------
100
+ # Event Tracking
101
+ # --------------------------------------------------------------------------
102
+
103
+ def track(
104
+ self,
105
+ event_name: str,
106
+ properties: Optional[Dict[str, Any]] = None,
107
+ category: str = "custom",
108
+ ) -> None:
109
+ self._send_event(event_name, properties, category)
110
+
111
+ def track_event(
112
+ self,
113
+ event_name: str,
114
+ properties: Optional[Dict[str, Any]] = None,
115
+ category: str = "custom",
116
+ ) -> None:
117
+ """Alias for track() — matches frontend SDK naming."""
118
+ self._send_event(event_name, properties, category)
119
+
120
+ # --------------------------------------------------------------------------
121
+ # Identity
122
+ # --------------------------------------------------------------------------
123
+
124
+ def identify(self, identify_id: str) -> IdentifyResult:
125
+ session = self._get_session()
126
+ self._session_manager.ensure_confirmed(session, self._http)
127
+
128
+ result = self._http.post_identify({
129
+ "siteId": self._site_id,
130
+ "sessionId": session.session_id,
131
+ "userId": session.distinct_id,
132
+ "distinctId": session.distinct_id,
133
+ "identifyId": identify_id,
134
+ })
135
+
136
+ new_distinct_id = result.get("newDistinctId") or result.get("distinctId")
137
+ if new_distinct_id and new_distinct_id != session.distinct_id:
138
+ session.distinct_id = new_distinct_id
139
+
140
+ return result
141
+
142
+ def wallet_address(self, wallet_addr: str) -> WalletAddressResult:
143
+ session = self._get_session()
144
+ self._session_manager.ensure_confirmed(session, self._http)
145
+
146
+ return self._http.post_wallet_address({
147
+ "siteId": self._site_id,
148
+ "sessionId": session.session_id,
149
+ "userId": session.distinct_id,
150
+ "distinctId": session.distinct_id,
151
+ "walletAddress": wallet_addr,
152
+ })
153
+
154
+ def reset(self) -> ResetResult:
155
+ session = self._get_session()
156
+ result = self._http.post_reset({
157
+ "siteId": self._site_id,
158
+ "sessionId": session.session_id,
159
+ "userId": session.distinct_id,
160
+ "distinctId": session.distinct_id,
161
+ })
162
+ self._session_manager.invalidate(self._distinct_id)
163
+ self._session = None
164
+ return result
165
+
166
+ # --------------------------------------------------------------------------
167
+ # People — accessible via user.people.set(...) etc.
168
+ # --------------------------------------------------------------------------
169
+
170
+ # --------------------------------------------------------------------------
171
+ # Groups
172
+ # --------------------------------------------------------------------------
173
+
174
+ def set_group(self, group_key: str, group_id: Union[str, List[str]]) -> ApiResult:
175
+ self._get_session()
176
+ return self._group_manager.set_group(group_key, group_id)
177
+
178
+ def add_group(self, group_key: str, group_id: str) -> ApiResult:
179
+ self._get_session()
180
+ return self._group_manager.add_group(group_key, group_id)
181
+
182
+ def remove_group(self, group_key: str, group_id: str) -> ApiResult:
183
+ self._get_session()
184
+ return self._group_manager.remove_group(group_key, group_id)
185
+
186
+ def get_group(self, group_key: str, group_id: str) -> GroupProfile:
187
+ return self._group_manager.get_group(group_key, group_id)
188
+
189
+ # --------------------------------------------------------------------------
190
+ # Error capture
191
+ # --------------------------------------------------------------------------
192
+
193
+ def capture_error(
194
+ self,
195
+ error: Exception,
196
+ severity: str = "error",
197
+ ) -> None:
198
+ session = self._get_session()
199
+ self._auto_event_manager.track_error(
200
+ error,
201
+ error_type=type(error).__name__,
202
+ severity=severity,
203
+ distinct_id=session.distinct_id,
204
+ )
205
+
206
+ # --------------------------------------------------------------------------
207
+ # Auto events (per-context toggle)
208
+ # --------------------------------------------------------------------------
209
+
210
+ def enable_auto_events(self) -> None:
211
+ self._auto_event_manager.enable()
212
+
213
+ def disable_auto_events(self) -> None:
214
+ self._auto_event_manager.disable()
215
+
216
+ # --------------------------------------------------------------------------
217
+ # Session info
218
+ # --------------------------------------------------------------------------
219
+
220
+ def get_session_id(self) -> Optional[str]:
221
+ return self._session.session_id if self._session else None
222
+
223
+ def get_current_distinct_id(self) -> str:
224
+ return self._get_distinct_id()