Skip to content

Commit 5dd87f2

Browse files
committed
Allowed a single Tracker instance to send events to multiple Emitters
- closes snowplow#91
1 parent b923c60 commit 5dd87f2

4 files changed

Lines changed: 32 additions & 32 deletions

File tree

CHANGES.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ Version 0.5.0 (2014-08-xx)
33
Converted payload values to strings for POST requests (#100)
44
Set content type to "application/json; charset=utf-8" for POST requests (#99)
55
Changed collector endpoint for POST to /com.snowplowanalytics.snowplow/tp2 (#98)
6-
Added ability to batch based on payload size (#95) (TODO)
76
Stopped setting and sending tid (#94)
87
Started setting and sending eid (#93)
9-
Allowed a single Tracker instance to send events to multiple Emitters (#91) (TODO)
8+
Allowed a single Tracker instance to send events to multiple Emitters (#91)
109

1110
Version 0.4.0 (2014-06-10)
1211
--------------------------

snowplow_tracker/test/integration/test_integration.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,23 @@ def fail_response_content(url, request):
6565
class IntegrationTest(unittest.TestCase):
6666

6767
def test_integration_page_view(self):
68-
t = tracker.Tracker(default_emitter, default_subject)
68+
t = tracker.Tracker([default_emitter], default_subject)
6969
with HTTMock(pass_response_content):
7070
t.track_page_view("http://savethearctic.org", "Save The Arctic", "http://referrer.com")
7171
expected_fields = {"e": "pv", "page": "Save+The+Arctic", "url": "http%3A%2F%2Fsavethearctic.org", "refr": "http%3A%2F%2Freferrer.com"}
7272
for key in expected_fields:
7373
self.assertEquals(from_querystring(key, querystrings[-1]), expected_fields[key])
7474

7575
def test_integration_ecommerce_transaction_item(self):
76-
t = tracker.Tracker(default_emitter, default_subject)
76+
t = tracker.Tracker([default_emitter], default_subject)
7777
with HTTMock(pass_response_content):
7878
t.track_ecommerce_transaction_item("12345", "pbz0025", 7.99, 2, "black-tarot", "tarot", currency="GBP")
7979
expected_fields = {"ti_ca": "tarot", "ti_id": "12345", "ti_qu": "2", "ti_sk": "pbz0025", "e": "ti", "ti_nm": "black-tarot", "ti_pr": "7.99", "ti_cu": "GBP"}
8080
for key in expected_fields:
8181
self.assertEquals(from_querystring(key, querystrings[-1]), expected_fields[key])
8282

8383
def test_integration_ecommerce_transaction(self):
84-
t = tracker.Tracker(default_emitter, default_subject)
84+
t = tracker.Tracker([default_emitter], default_subject)
8585
with HTTMock(pass_response_content):
8686
t.track_ecommerce_transaction("6a8078be", 35, city="London", currency="GBP", items=
8787
[{
@@ -110,23 +110,23 @@ def test_integration_ecommerce_transaction(self):
110110
self.assertEquals(from_querystring("dtm", querystrings[-3]), from_querystring("dtm", querystrings[-2]))
111111

112112
def test_integration_screen_view(self):
113-
t = tracker.Tracker(default_emitter, default_subject)
113+
t = tracker.Tracker([default_emitter], default_subject)
114114
with HTTMock(pass_response_content):
115115
t.track_screen_view("Game HUD 2", "Hello!")
116116
expected_fields = {"e": "ue"}
117117
for key in expected_fields:
118118
self.assertEquals(from_querystring(key, querystrings[-1]), expected_fields[key])
119119

120120
def test_integration_struct_event(self):
121-
t = tracker.Tracker(default_emitter, default_subject)
121+
t = tracker.Tracker([default_emitter], default_subject)
122122
with HTTMock(pass_response_content):
123123
t.track_struct_event("Ecomm", "add-to-basket", "dog-skateboarding-video", "hd", 13.99)
124124
expected_fields = {"se_ca": "Ecomm", "se_pr": "hd", "se_la": "dog-skateboarding-video", "se_va": "13.99", "se_ac": "add-to-basket", "e": "se"}
125125
for key in expected_fields:
126126
self.assertEquals(from_querystring(key, querystrings[-1]), expected_fields[key])
127127

128128
def test_integration_unstruct_event_non_base64(self):
129-
t = tracker.Tracker(default_emitter, default_subject, encode_base64=False)
129+
t = tracker.Tracker([default_emitter], default_subject, encode_base64=False)
130130
with HTTMock(pass_response_content):
131131
t.track_unstruct_event({"schema": "iglu:com.acme/viewed_product/jsonschema/2-0-2", "data": {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": 1000}})
132132
expected_fields = {"e": "ue"}
@@ -140,7 +140,7 @@ def test_integration_unstruct_event_non_base64(self):
140140
})
141141

142142
def test_integration_unstruct_event_base64(self):
143-
t = tracker.Tracker(default_emitter, default_subject, encode_base64=True)
143+
t = tracker.Tracker([default_emitter], default_subject, encode_base64=True)
144144
with HTTMock(pass_response_content):
145145
t.track_unstruct_event({"schema": "iglu:com.acme/viewed_product/jsonschema/2-0-2", "data": {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": 1000}})
146146
expected_fields = {"e": "ue"}
@@ -154,7 +154,7 @@ def test_integration_unstruct_event_base64(self):
154154
})
155155

156156
def test_integration_context_non_base64(self):
157-
t = tracker.Tracker(default_emitter, default_subject, encode_base64=False)
157+
t = tracker.Tracker([default_emitter], default_subject, encode_base64=False)
158158
with HTTMock(pass_response_content):
159159
t.track_page_view("localhost", "local host", None, [{"schema": "iglu:com.example/user/jsonschema/2-0-3", "data": {"user_type": "tester"}}])
160160
envelope_string = from_querystring("co", querystrings[-1])
@@ -165,7 +165,7 @@ def test_integration_context_non_base64(self):
165165
})
166166

167167
def test_integration_context_base64(self):
168-
t = tracker.Tracker(default_emitter, default_subject, encode_base64=True)
168+
t = tracker.Tracker([default_emitter], default_subject, encode_base64=True)
169169
with HTTMock(pass_response_content):
170170
t.track_page_view("localhost", "local host", None, [{"schema": "iglu:com.example/user/jsonschema/2-0-3", "data": {"user_type": "tester"}}])
171171
envelope_string = unquote_plus(from_querystring("cx", querystrings[-1]))
@@ -184,7 +184,7 @@ def test_integration_standard_nv_pairs(self):
184184
s.set_timezone("Europe London")
185185
s.set_lang("en")
186186

187-
t = tracker.Tracker(emitters.Emitter("localhost"), s, "cf", app_id="angry-birds-android")
187+
t = tracker.Tracker([emitters.Emitter("localhost")], s, "cf", app_id="angry-birds-android")
188188
with HTTMock(pass_response_content):
189189
t.track_page_view("localhost", "local host")
190190
expected_fields = {"tna": "cf", "res": "100x200",
@@ -197,15 +197,15 @@ def test_integration_standard_nv_pairs(self):
197197

198198
def test_integration_redis_default(self):
199199
r = redis.StrictRedis()
200-
t = tracker.Tracker(emitters.RedisEmitter(), default_subject)
200+
t = tracker.Tracker([emitters.RedisEmitter()], default_subject)
201201
t.track_page_view("http://www.example.com")
202202
event_string = r.rpop("snowplow")
203203
event_dict = json.loads(event_string.decode("utf-8"))
204204
self.assertEquals(event_dict["e"], "pv")
205205

206206
def test_integration_redis_custom(self):
207207
r = redis.StrictRedis(db=1)
208-
t = tracker.Tracker(emitters.RedisEmitter(rdb=r, key="custom_key"), default_subject)
208+
t = tracker.Tracker([emitters.RedisEmitter(rdb=r, key="custom_key")], default_subject)
209209
t.track_page_view("http://www.example.com")
210210
event_string = r.rpop("custom_key")
211211
event_dict = json.loads(event_string.decode("utf-8"))
@@ -216,7 +216,7 @@ def test_integration_success_callback(self):
216216
callback_failure_queue = []
217217
callback_emitter = emitters.Emitter("localhost", on_success=lambda x: callback_success_queue.append(x),
218218
on_failure=lambda x, y:callback_failure_queue.append(x))
219-
t = tracker.Tracker(callback_emitter, default_subject)
219+
t = tracker.Tracker([callback_emitter], default_subject)
220220
with HTTMock(pass_response_content):
221221
t.track_page_view("http://www.example.com")
222222
self.assertEquals(callback_success_queue[0], 1)
@@ -227,7 +227,7 @@ def test_integration_failure_callback(self):
227227
callback_failure_queue = []
228228
callback_emitter = emitters.Emitter("localhost", on_success=lambda x: callback_success_queue.append(x),
229229
on_failure=lambda x, y:callback_failure_queue.append(x))
230-
t = tracker.Tracker(callback_emitter, default_subject)
230+
t = tracker.Tracker([callback_emitter], default_subject)
231231
with HTTMock(fail_response_content):
232232
t.track_page_view("http://www.example.com")
233233
self.assertEquals(callback_success_queue, [])

snowplow_tracker/test/unit/test_tracker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def setUp(self):
3333
pass
3434

3535
def test_initialisation(self):
36-
t = Tracker(Emitter("d3rkrsqld9gmqf.cloudfront.net"), namespace="cloudfront", encode_base64= False, app_id="AF003")
36+
t = Tracker([Emitter("d3rkrsqld9gmqf.cloudfront.net")], namespace="cloudfront", encode_base64= False, app_id="AF003")
3737
self.assertEquals(t.standard_nv_pairs["tna"], "cloudfront")
3838
self.assertEquals(t.standard_nv_pairs["aid"], "AF003")
3939
self.assertEquals(t.encode_base64, False)

snowplow_tracker/tracker.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ class Tracker:
5555
new_contract("emitter", lambda s: hasattr(s, "input"))
5656

5757
@contract
58-
def __init__(self, emitter, subject=None,
58+
def __init__(self, emitters, subject=None,
5959
namespace=None, app_id=None, encode_base64=DEFAULT_ENCODE_BASE64):
6060
"""
61-
:param emitter: Emitter to which events will be sent
62-
:type emitter: emitter
61+
:param emitters: Emitters to which events will be sent
62+
:type emitters: list[>0](emitter)
6363
:param subject: Subject to be tracked
6464
:type subject: subject | None
6565
:param namespace: Identifier for the Tracker instance
@@ -72,7 +72,7 @@ def __init__(self, emitter, subject=None,
7272
if subject is None:
7373
subject = _subject.Subject()
7474

75-
self.emitter = emitter
75+
self.emitters = emitters
7676
self.subject = subject
7777
self.encode_base64 = encode_base64
7878

@@ -119,11 +119,12 @@ def track(self, pb):
119119
120120
:param pb: Payload builder
121121
:type pb: payload
122-
:rtype: tracker | int
122+
:rtype: tracker | list(int)
123123
"""
124-
result = self.emitter.input(pb.nv_pairs)
125-
if result is not None:
126-
return result
124+
results = [emitter.input(pb.nv_pairs) for emitter in self.emitters]
125+
filtered = [res for res in results if res is not None]
126+
if filtered:
127+
return filtered
127128
else:
128129
return self
129130

@@ -139,7 +140,7 @@ def complete_payload(self, pb, context, tstamp):
139140
:type context: list(dict(string:*)) | None
140141
:param tstamp: Optional user-provided timestamp for the event
141142
:type tstamp: int | float | None
142-
:rtype: tracker | int
143+
:rtype: tracker | list(int)
143144
"""
144145
pb.add("eid", Tracker.get_uuid())
145146
pb.add("dtm", Tracker.get_timestamp(tstamp))
@@ -164,7 +165,7 @@ def track_page_view(self, page_url, page_title=None, referrer=None, context=None
164165
:type referrer: string_or_none
165166
:param context: Custom context for the event
166167
:type context: list(dict(string:*)) | None
167-
:rtype: tracker | int
168+
:rtype: tracker | list(int)
168169
"""
169170
pb = payload.Payload()
170171
pb.add("e", "pv") # pv: page view
@@ -199,7 +200,7 @@ def track_ecommerce_transaction_item(self, order_id, sku, price, quantity,
199200
:type currency: string_or_none
200201
:param context: Custom context for the event
201202
:type context: list(dict(string:*)) | None
202-
:rtype: tracker | int
203+
:rtype: tracker | list(int)
203204
"""
204205
pb = payload.Payload()
205206
pb.add("e", "ti")
@@ -282,7 +283,7 @@ def track_screen_view(self, name, id_=None, context=None, tstamp=None):
282283
:type id_: string_or_none
283284
:param context: Custom context for the event
284285
:type context: list(dict(string:*)) | None
285-
:rtype: tracker | int
286+
:rtype: tracker | list(int)
286287
"""
287288
screen_view_properties = {"name": name}
288289
if id_ is not None:
@@ -313,7 +314,7 @@ def track_struct_event(self, category, action, label=None, property_=None, value
313314
:type value: int | float | None
314315
:param context: Custom context for the event
315316
:type context: list(dict(string:*)) | None
316-
:rtype: tracker | int
317+
:rtype: tracker | list(int)
317318
"""
318319
pb = payload.Payload()
319320
pb.add("e", "se")
@@ -335,7 +336,7 @@ def track_unstruct_event(self, event_json, context=None, tstamp=None):
335336
:type event_json: dict(string: string | dict)
336337
:param context: Custom context for the event
337338
:type context: list(dict(string:*)) | None
338-
:rtype: tracker | int
339+
:rtype: tracker | list(int)
339340
"""
340341

341342
envelope = {"schema": UNSTRUCT_EVENT_SCHEMA , "data": event_json}
@@ -354,7 +355,7 @@ def flush(self, async=False):
354355
355356
:param async: Whether the flush is done asynchronously. Default is False
356357
:type async: bool
357-
:rtype: tracker | int
358+
:rtype: tracker | list(int)
358359
"""
359360
if async:
360361
self.emitter.flush()

0 commit comments

Comments
 (0)