Skip to content

Commit 6ed0d93

Browse files
author
Keyur
committed
Add: User/Company sampling for outgoing events
Add: User/Company sampling for outgoing events Add: Weight field to event Refactor: Update moesifapi version to 1.3.2 Bump version to 0.2.0
1 parent 4340a63 commit 6ed0d93

File tree

8 files changed

+144
-32
lines changed

8 files changed

+144
-32
lines changed

moesifpythonrequest/app_config/__init__.py

Whitespace-only changes.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from datetime import datetime
2+
from moesifapi.exceptions.api_exception import *
3+
import json
4+
5+
6+
# Application Configuration
7+
class AppConfig:
8+
9+
def get_config(self, api_client, debug):
10+
"""Get Config"""
11+
try:
12+
config_api_response = api_client.get_app_config()
13+
return config_api_response
14+
except APIException as inst:
15+
if 401 <= inst.response_code <= 403:
16+
print("Unauthorized access getting application configuration. Please check your Appplication Id.")
17+
if debug:
18+
print("Error getting application configuration, with status code:")
19+
print(inst.response_code)
20+
21+
def parse_configuration(self, config, debug):
22+
"""Parse configuration object and return Etag, sample rate and last updated time"""
23+
try:
24+
return config.headers.get("X-Moesif-Config-ETag"), json.loads(config.raw_body).get('sample_rate', 100), datetime.utcnow()
25+
except:
26+
if debug:
27+
print('Error while parsing the configuration object, setting the sample rate to default')
28+
return None, 100, datetime.utcnow()
29+
30+
def get_sampling_percentage(self, config, user_id, company_id):
31+
"""Get sampling percentage"""
32+
33+
config_body = json.loads(config.raw_body)
34+
35+
user_sample_rate = config_body.get('user_sample_rate', None)
36+
37+
company_sample_rate = config_body.get('company_sample_rate', None)
38+
39+
if user_id and user_sample_rate and user_id in user_sample_rate:
40+
return user_sample_rate[user_id]
41+
42+
if company_id and company_sample_rate and company_id in company_sample_rate:
43+
return company_sample_rate[company_id]
44+
45+
return config_body.get('sample_rate', 100)

moesifpythonrequest/global_variables.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""This module is to declare global objects."""
2+
from datetime import datetime
23

34
# Configuration Options
45
global moesif_options
@@ -11,3 +12,27 @@
1112
# Patch Flag
1213
global MOESIF_PATCH
1314
MOESIF_PATCH = False
15+
16+
# MoesifAPI Client
17+
global api_client
18+
api_client = None
19+
20+
# App Config class
21+
global app_config
22+
app_config = None
23+
24+
# App Config
25+
global config
26+
config = None
27+
28+
# App Config sampling percentage
29+
global sampling_percentage
30+
sampling_percentage = 100
31+
32+
# App Config eTag
33+
global config_etag
34+
config_etag = None
35+
36+
# App Config last updated time
37+
global last_updated_time
38+
last_updated_time = datetime.utcnow()

moesifpythonrequest/patch_request/patch_request.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from ..utility_function.utility_function import UtilityFunction
44
from ..outgoing_recorder.outgoing_recorder import OutgoingRecorder
55
import requests
6-
from .. import global_variables
6+
from .. import global_variables as gv
77
from requests import sessions
88

99

@@ -38,10 +38,10 @@ def new_session_function(session, method, url, params=None, data=None, headers=N
3838
end_time = utility_function.get_current_time()
3939

4040
if not utility_function.is_moesif(headers, url):
41-
generated_recorder = outgoing_recorder.prepare_recorder(global_variables.moesif_options, response.request, response, start_time, end_time)
41+
generated_recorder = outgoing_recorder.prepare_recorder(gv.moesif_options, response.request, response, start_time, end_time)
4242

4343
if isinstance(generated_recorder, EventModel):
44-
moesif_response = recorder(global_variables.moesif_options.get('APPLICATION_ID'), generated_recorder)
44+
moesif_response = recorder(generated_recorder)
4545

4646
return response
4747

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,63 @@
11
# Import Libraries
22
from moesifapi.exceptions.api_exception import APIException
3-
from moesifapi.moesif_api_client import MoesifAPIClient, Configuration
4-
from .. import global_variables
3+
from .. import global_variables as gv
4+
from datetime import datetime, timedelta
55
import threading
6+
import random
7+
import math
68

79

810
class SendMoesif():
911
# Function to send event to Moesif
10-
def send_event(self, application_id, event_model):
11-
if global_variables.moesif_options.get('LOCAL_DEBUG', False):
12-
Configuration.BASE_URI = global_variables.moesif_options.get('LOCAL_MOESIF_BASEURL', 'https://api.moesif.net')
13-
14-
client = MoesifAPIClient(application_id)
15-
api_client = client.api
12+
def send_event(self, event_model):
1613
try:
17-
if global_variables.DEBUG:
14+
if gv.DEBUG:
1815
print('Calling API to create event')
19-
api_client.create_event(event_model)
20-
if global_variables.DEBUG:
21-
print("sent done")
16+
event_api_response = gv.api_client.create_event(event_model)
17+
event_response_config_etag = event_api_response.get("X-Moesif-Config-ETag")
18+
19+
if event_response_config_etag is not None \
20+
and gv.config_etag is not None \
21+
and gv.config_etag != event_response_config_etag \
22+
and datetime.utcnow() > gv.last_updated_time + timedelta(minutes=5):
23+
try:
24+
gv.config = gv.app_config.get_config(gv.api_client, gv.DEBUG)
25+
self.config_etag, self.sampling_percentage, self.last_updated_time = gv.app_config.parse_configuration(
26+
gv.config, gv.DEBUG)
27+
except:
28+
if gv.DEBUG:
29+
print('Error while updating the application configuration')
30+
if gv.DEBUG:
31+
print("Event sent successfully")
2232
except APIException as inst:
2333
if 401 <= inst.response_code <= 403:
2434
print("Unauthorized access sending event to Moesif. Please check your Appplication Id.")
25-
if global_variables.DEBUG:
35+
if gv.DEBUG:
2636
print("Error sending event to Moesif, with status code:")
2737
print(inst.response_code)
2838

2939
# Function to send event async
30-
def send_moesif_async(self, applicaiton_id, event_model):
40+
def send_moesif_async(self, event_model):
3141
try:
32-
mask_event_model = global_variables.moesif_options.get('MASK_EVENT_MODEL', None)
42+
mask_event_model = gv.moesif_options.get('MASK_EVENT_MODEL', None)
3343
if mask_event_model is not None:
34-
if global_variables.DEBUG:
44+
if gv.DEBUG:
3545
print('Masking the event')
3646
event_model = mask_event_model(event_model)
3747
except:
38-
if global_variables.DEBUG:
48+
if gv.DEBUG:
3949
print("Can not execute MASK_EVENT_MODEL function. Please check moesif settings.")
4050

41-
sending_background_thread = threading.Thread(target=self.send_event, args=(applicaiton_id, event_model,))
42-
if global_variables.DEBUG:
43-
print('Staring a new thread')
44-
sending_background_thread.start()
51+
random_percentage = random.random() * 100
52+
gv.sampling_percentage = gv.app_config.get_sampling_percentage(gv.config, event_model.user_id, event_model.company_id)
53+
54+
if gv.sampling_percentage >= random_percentage:
55+
event_model.weight = 1 if gv.sampling_percentage == 0 else math.floor(100 / gv.sampling_percentage)
56+
sending_background_thread = threading.Thread(target=self.send_event, args=(event_model,))
57+
if gv.DEBUG:
58+
print('Staring a new thread')
59+
sending_background_thread.start()
60+
else:
61+
if gv.DEBUG:
62+
print('Skipped Event due to sampling percentage: ' + str(
63+
gv.sampling_percentage) + ' and random percentage: ' + str(random_percentage))
Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,49 @@
11
# Import Libraries
2-
from .. import global_variables
2+
from .. import global_variables as gv
33
from ..patch_request.patch_request import PatchRequest
44
from ..send_moesif.send_moesif import SendMoesif
5-
5+
from ..app_config.app_config import AppConfig
6+
from moesifapi.moesif_api_client import MoesifAPIClient, Configuration
67

78
class StartCapture():
89

910
# Start capturing the outgoing requests
1011
def start_capture_outgoing(self, options):
1112

1213
# Check if the moesif_options are of dict type
13-
global_variables.moesif_options = options
14-
global_variables.DEBUG = global_variables.moesif_options.get('LOCAL_DEBUG', False)
14+
gv.moesif_options = options
15+
gv.DEBUG = gv.moesif_options.get('LOCAL_DEBUG', False)
1516

16-
if global_variables.MOESIF_PATCH:
17+
if gv.MOESIF_PATCH:
1718
print('Already started patching the outgoing requests')
1819
else:
1920
print('Starting to patch the outgoing requests')
2021

21-
global_variables.MOESIF_PATCH = True
22+
gv.MOESIF_PATCH = True
2223
# Create an instance of the class
2324
patch_instance = PatchRequest()
2425
send_async = SendMoesif()
26+
gv.app_config = AppConfig()
27+
28+
if gv.DEBUG:
29+
Configuration.BASE_URI = gv.moesif_options.get('LOCAL_MOESIF_BASEURL', 'https://api.moesif.net')
30+
31+
# Get the MoesifAPI client
32+
if gv.moesif_options.get('APPLICATION_ID', None):
33+
gv.api_client = MoesifAPIClient(gv.moesif_options.get('APPLICATION_ID')).api
34+
else:
35+
raise Exception('Moesif Application ID is required in moesif options')
36+
37+
# Get the application config
38+
gv.config = gv.app_config.get_config(gv.api_client, gv.DEBUG)
39+
40+
# Parse the application config
41+
try:
42+
if gv.config:
43+
gv.config_etag, gv.sampling_percentage, gv.last_updated_time = gv.app_config.parse_configuration(
44+
gv.config, gv.DEBUG)
45+
except:
46+
if gv.DEBUG:
47+
print('Error while parsing application configuration on initialization')
2548

2649
_unpatch = patch_instance.patch(send_async.send_moesif_async)

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
requests==2.20.0
2-
moesifapi==1.3.1
2+
moesifapi==1.3.2

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
# Versions should comply with PEP440. For a discussion on single-sourcing
2929
# the version across setup.py and the project code, see
3030
# https://packaging.python.org/en/latest/single_source_version.html
31-
version='0.1.12',
31+
version='0.2.0',
3232

3333
description='Moesif Python request',
3434
long_description=long_description,

0 commit comments

Comments
 (0)