Skip to content

Commit 364f70d

Browse files
sakshamg1304rohitesh-wingify
authored andcommitted
feat: usage stats during initialization
1 parent 98e27ed commit 364f70d

File tree

8 files changed

+118
-26
lines changed

8 files changed

+118
-26
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.12.0] - 2025-09-02
8+
9+
### Added
10+
11+
- Sends usage statistics to VWO servers automatically during SDK initialization
12+
713
## [1.11.0] - 2025-07-21
814

915
### Added

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def run(self):
121121

122122
setup(
123123
name="vwo-fme-python-sdk",
124-
version="1.11.0",
124+
version="1.12.0",
125125
description="VWO Feature Management and Experimentation SDK for Python",
126126
long_description=long_description,
127127
long_description_content_type="text/markdown",

vwo/constants/Constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Constants:
1717
# TODO: read from setup.py
1818
sdk_meta = {
1919
"name": "vwo-fme-python-sdk",
20-
"version": "1.11.0"
20+
"version": "1.12.0"
2121
}
2222

2323
# Constants
@@ -62,3 +62,4 @@ class Constants:
6262

6363
MAX_RETRIES = 3
6464
INITIAL_WAIT_TIME = 2
65+
PRODUCT_NAME = "fme"

vwo/enums/event_enum.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ class EventEnum(Enum):
2020
VWO_VARIATION_SHOWN = "vwo_variationShown"
2121
VWO_SYNC_VISITOR_PROP = "vwo_syncVisitorProp"
2222
VWO_LOG_EVENT = "vwo_log"
23-
VWO_SDK_INIT_EVENT = "vwo_fmeSdkInit"
23+
VWO_SDK_INIT_EVENT = "vwo_fmeSdkInit"
24+
VWO_USAGE_STATS = "vwo_sdkUsageStats"

vwo/utils/event_util.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from typing import Optional
16-
from .network_util import get_events_base_properties, get_sdk_init_event_payload, send_event
16+
from .network_util import get_events_base_properties, get_sdk_init_event_payload, get_sdk_usage_stats_event_payload, send_event
1717
from ..enums.event_enum import EventEnum
1818
from ..vwo_client import VWOClient
1919
from ..packages.logger.core.log_manager import LogManager
@@ -50,4 +50,29 @@ def send_sdk_init_event(settings_fetch_time: Optional[int] = None, sdk_init_time
5050
err=str(e)
5151
)
5252
)
53-
pass
53+
pass
54+
55+
56+
def send_sdk_usage_stats_event(usage_stats_account_id: int) -> None:
57+
"""
58+
Sends a usage stats event to VWO.
59+
This event is triggered when the SDK is initialized.
60+
61+
:param usage_stats_account_id: Account ID for usage stats event
62+
"""
63+
# Create the query parameters
64+
properties = get_events_base_properties(EventEnum.VWO_USAGE_STATS.value, "", "", True, usage_stats_account_id)
65+
66+
# Create the payload with required fields
67+
payload = get_sdk_usage_stats_event_payload(EventEnum.VWO_USAGE_STATS.value, usage_stats_account_id)
68+
69+
vwo_instance = VWOClient.get_instance()
70+
71+
# Check if batch events are enabled
72+
if vwo_instance.batch_event_queue is not None and vwo_instance.batch_event_queue.is_enabled():
73+
# Enqueue the event to the batch queue
74+
vwo_instance.batch_event_queue.enqueue(payload)
75+
else:
76+
# Send the event immediately if batch events are not enabled
77+
send_event(properties, payload, EventEnum.VWO_USAGE_STATS.value)
78+

vwo/utils/network_util.py

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def get_event_batching_query_params(account_id: str) -> Dict[str, Any]:
5959

6060
# Function to build generic properties for tracking events
6161
def get_events_base_properties(
62-
event_name: str, visitor_user_agent: str = "", ip_address: str = ""
62+
event_name: str, visitor_user_agent: str = "", ip_address: str = "", is_usage_stats_event: bool = False, usage_stats_account_id: int = None
6363
) -> Dict[str, Any]:
6464

6565
# Get the instance of SettingsManager
@@ -69,10 +69,9 @@ def get_events_base_properties(
6969
sdk_key = settings.get_sdk_key()
7070
account_id = settings.get_account_id()
7171

72-
return {
72+
properties = {
7373
"en": event_name,
7474
"a": account_id,
75-
"env": sdk_key,
7675
"eTime": get_current_unix_timestamp_in_millis(),
7776
"random": get_random_number(),
7877
"p": "FS",
@@ -83,6 +82,14 @@ def get_events_base_properties(
8382
+ UrlEnum.EVENTS.value,
8483
}
8584

85+
if not is_usage_stats_event:
86+
# set env key for standard sdk events
87+
properties["env"] = sdk_key
88+
else:
89+
# set account id for internal usage stats event
90+
properties["a"] = str(usage_stats_account_id)
91+
return properties
92+
8693

8794
# Function to build payload for tracking events
8895
def _get_event_base_payload(
@@ -91,10 +98,27 @@ def _get_event_base_payload(
9198
event_name: str,
9299
visitor_user_agent: str = "",
93100
ip_address: str = "",
101+
is_usage_stats_event: bool = False,
102+
usage_stats_account_id: int = None,
94103
) -> Dict[str, Any]:
95104

96-
uuid_value = get_uuid(user_id, SettingsManager.get_instance().get_account_id())
105+
account_id = SettingsManager.get_instance().get_account_id()
106+
if is_usage_stats_event:
107+
# set account id for internal usage stats event
108+
account_id = usage_stats_account_id
109+
110+
uuid_value = get_uuid(str(user_id), str(account_id))
97111
sdk_key = SettingsManager.get_instance().get_sdk_key()
112+
113+
props = {
114+
"vwo_sdkName": Constants.SDK_NAME,
115+
"vwo_sdkVersion": Constants.SDK_VERSION,
116+
}
117+
118+
if not is_usage_stats_event:
119+
# set env key for standard sdk events
120+
props["vwo_envKey"] = sdk_key
121+
98122
properties = {
99123
"d": {
100124
"msgId": f"{uuid_value}-{get_current_unix_timestamp_in_millis()}",
@@ -103,18 +127,21 @@ def _get_event_base_payload(
103127
"visitor_ua": visitor_user_agent,
104128
"visitor_ip": ip_address,
105129
"event": {
106-
"props": {
107-
"vwo_sdkName": Constants.SDK_NAME,
108-
"vwo_sdkVersion": Constants.SDK_VERSION,
109-
"vwo_envKey": sdk_key,
110-
},
130+
"props": props,
111131
"name": event_name,
112132
"time": get_current_unix_timestamp_in_millis(),
113133
},
114-
"visitor": {"props": {Constants.VWO_FS_ENVIRONMENT: sdk_key}},
115134
}
116135
}
117136

137+
if not is_usage_stats_event:
138+
# set visitor props for standard sdk events
139+
properties["d"]["visitor"] = {
140+
"props": {
141+
Constants.VWO_FS_ENVIRONMENT: sdk_key,
142+
},
143+
}
144+
118145
return properties
119146

120147

@@ -136,10 +163,6 @@ def get_track_user_payload_data(
136163
properties["d"]["event"]["props"]["variation"] = str(variation_id)
137164
properties["d"]["event"]["props"]["isFirst"] = 1
138165

139-
usage_stats_data = UsageStatsUtil().get_usage_stats()
140-
if len(usage_stats_data) > 0:
141-
properties["d"]["event"]["props"]["vwoMeta"] = usage_stats_data
142-
143166
LogManager.get_instance().debug(
144167
debug_messages.get("IMPRESSION_FOR_TRACK_USER").format(
145168
accountId=settings.get_account_id(), userId=user_id, campaignId=campaign_id
@@ -352,7 +375,7 @@ def get_messaging_event_payload(
352375
properties["d"]["event"]["props"]["vwo_envKey"] = settings.get_sdk_key()
353376
properties["d"]["event"]["props"][
354377
"product"
355-
] = "fme" # Assuming 'product' is a required field
378+
] = Constants.PRODUCT_NAME
356379

357380
# Set the message data
358381
data = {
@@ -392,7 +415,7 @@ def get_sdk_init_event_payload(
392415

393416
# Set the required fields as specified
394417
properties["d"]["event"]["props"][Constants.VWO_FS_ENVIRONMENT] = settings.get_sdk_key()
395-
properties["d"]["event"]["props"]["product"] = "fme"
418+
properties["d"]["event"]["props"]["product"] = Constants.PRODUCT_NAME
396419

397420
data = {
398421
"isSDKInitialized": True,
@@ -404,12 +427,37 @@ def get_sdk_init_event_payload(
404427
return properties
405428

406429

430+
def get_sdk_usage_stats_event_payload(
431+
event_name: str, usage_stats_account_id: int
432+
) -> Dict[str, Any]:
433+
"""
434+
Constructs the payload for sdk usage stats event.
435+
436+
Args:
437+
event_name: The name of the event.
438+
usage_stats_account_id: Account ID for usage stats event.
439+
440+
Returns:
441+
The constructed payload with required fields.
442+
"""
443+
# Get user ID and properties
444+
settings = SettingsManager.get_instance()
445+
user_id = f"{settings.get_account_id()}_{settings.get_sdk_key()}"
446+
properties = _get_event_base_payload(None, user_id, event_name, None, None, True, usage_stats_account_id)
447+
448+
# Set the required fields as specified
449+
properties["d"]["event"]["props"]["product"] = Constants.PRODUCT_NAME
450+
properties["d"]["event"]["props"]["vwoMeta"] = UsageStatsUtil().get_usage_stats()
451+
452+
return properties
453+
454+
407455
def send_event(
408456
properties: Dict[str, Any], payload: Dict[str, Any], event_name: str
409457
) -> Dict[str, Any]:
410458
try:
411459
#if event_name is not VWO_LOG_EVENT, then set base url to constants.hostname
412-
if event_name == EventEnum.VWO_LOG_EVENT.value:
460+
if event_name == EventEnum.VWO_LOG_EVENT.value or event_name == EventEnum.VWO_USAGE_STATS.value:
413461
base_url = Constants.HOST_NAME
414462
protocol = Constants.HTTPS_PROTOCOL
415463
port = 443

vwo/utils/usage_stats_util.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import sys
1717
from ..constants.Constants import Constants
1818
from ..packages.logger.enums.log_level_number_enum import LogLevelNumberEnum
19-
19+
from ..services.settings_manager import SettingsManager
2020

2121
class UsageStatsUtil:
2222
"""
@@ -45,17 +45,19 @@ def set_usage_stats(self, options: Dict[str, Any]) -> None:
4545
options (Dict[str, Any]): Configuration options for the SDK containing:
4646
- storage: Storage service configuration
4747
- logger: Logger configuration
48-
- batch_events: Event batching configuration
48+
- batch_event_data: Event batching configuration
4949
- integrations: Integrations configuration
5050
- polling_interval: Polling interval configuration
5151
- sdk_name: SDK name configuration
5252
"""
5353
data: Dict[str, Union[str, int]] = {}
54+
data["a"] = SettingsManager.get_instance().get_account_id()
55+
data["env"] = SettingsManager.get_instance().get_sdk_key()
5456

5557
# Map configuration options to usage stats flags
5658
if options.get("integrations"):
5759
data["ig"] = 1 # Integration enabled
58-
if options.get("batch_events"):
60+
if options.get("batch_event_data"):
5961
data["eb"] = 1 # Event batching enabled
6062

6163
if options.get("gateway_service"):

vwo/vwo.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from vwo.vwo_builder import VWOBuilder
1919
from vwo.vwo_client import VWOClient
2020
from vwo.enums.url_enum import UrlEnum
21-
from vwo.utils.event_util import send_sdk_init_event
21+
from vwo.utils.event_util import send_sdk_init_event, send_sdk_usage_stats_event
2222

2323

2424
class VWO:
@@ -83,6 +83,15 @@ def init(options: Dict[str, Any]) -> Optional["VWOClient"]:
8383
if instance.is_settings_valid_on_init and not was_initialized:
8484
send_sdk_init_event(instance.settings_fetch_time, sdk_init_time)
8585

86+
# send sdk usage stats event
87+
# get usage stats account id from settings
88+
usage_stats_account_id = None
89+
if instance.original_settings is not None:
90+
usage_stats_account_id = instance.original_settings.get("usageStatsAccountId")
91+
92+
if usage_stats_account_id:
93+
send_sdk_usage_stats_event(usage_stats_account_id)
94+
8695
return instance
8796
except Exception as e:
8897
print("VWO initialization failed. Error:", e)

0 commit comments

Comments
 (0)