Skip to content

Commit e85a952

Browse files
authored
Merge pull request #4 from aptabase/fix/auto_gen_session_id
fix: auto-gen session id
2 parents f1f4e84 + f7993a9 commit e85a952

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "aptabase"
3-
version = "0.0.2"
3+
version = "0.0.3"
44
description = "Python SDK for Aptabase analytics"
55
readme = "README.md"
66
requires-python = ">=3.11"

src/aptabase/client.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import asyncio
66
import logging
7+
from datetime import datetime, timedelta
78
from typing import Any
89
from urllib.parse import urljoin
910

@@ -20,6 +21,8 @@
2021
"SH": None, # Self-hosted, requires custom base_url in options
2122
}
2223

24+
_SESSION_TIMEOUT = timedelta(hours=1)
25+
2326

2427
class Aptabase:
2528
"""Aptabase analytics client."""
@@ -71,6 +74,7 @@ def __init__(
7174
self._client: httpx.AsyncClient | None = None
7275
self._flush_task: asyncio.Task[Any] | None = None
7376
self._session_id: str | None = None
77+
self._last_touched: datetime | None = None
7478

7579
def _get_base_url(self, app_key: str) -> str:
7680
"""Determine the base URL from the app key."""
@@ -132,13 +136,7 @@ async def stop(self) -> None:
132136
await self._client.aclose()
133137
self._client = None
134138

135-
async def track(
136-
self,
137-
event_name: str,
138-
props: dict[str, Any] | None = None,
139-
*,
140-
session_id: str | None = None,
141-
) -> None:
139+
async def track(self, event_name: str, props: dict[str, Any] | None = None) -> None:
142140
"""Track an analytics event.
143141
144142
Args:
@@ -152,15 +150,17 @@ async def track(
152150
if props is not None and not isinstance(props, dict):
153151
raise ValidationError("Event properties must be a dictionary")
154152

153+
# Get or create session (handles timeout automatically)
154+
session_id = self._get_or_create_session()
155+
155156
event = Event(
156157
name=event_name,
157158
props=props,
158-
session_id=session_id or self._session_id,
159+
session_id=session_id,
159160
)
160161

161162
async with self._queue_lock:
162163
self._event_queue.append(event)
163-
164164
# Auto-flush if we reach the batch size
165165
if len(self._event_queue) >= self._max_batch_size:
166166
await self._flush_events()
@@ -216,8 +216,26 @@ async def _periodic_flush(self) -> None:
216216
except asyncio.CancelledError:
217217
pass
218218

219-
def set_session_id(self, session_id: str) -> None:
220-
"""Set the session ID for future events."""
221-
if not session_id or not isinstance(session_id, str):
222-
raise ValidationError("Session ID must be a non-empty string")
223-
self._session_id = session_id
219+
def _get_or_create_session(self) -> str:
220+
"""Get current session or create new one if expired."""
221+
now = datetime.now()
222+
223+
if self._session_id is None or self._last_touched is None:
224+
self._session_id = self._new_session_id()
225+
self._last_touched = now
226+
elif now - self._last_touched > _SESSION_TIMEOUT:
227+
self._session_id = self._new_session_id()
228+
self._last_touched = now
229+
else:
230+
self._last_touched = now
231+
232+
return self._session_id
233+
234+
@staticmethod
235+
def _new_session_id() -> str:
236+
"""Generate a new session ID."""
237+
import random
238+
239+
epoch_seconds = int(datetime.now().timestamp())
240+
random_part = random.randint(0, 99999999)
241+
return str(epoch_seconds * 100000000 + random_part)

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)