Skip to content

Commit b546a7f

Browse files
committed
Merge pull request #66 from snowplow/feature/0.2.0
Feature/0.2.0
2 parents b50b035 + 19d3532 commit b546a7f

File tree

13 files changed

+207
-235
lines changed

13 files changed

+207
-235
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
language: python
22
python:
3+
- "2.7"
34
- "3.3"
45
# command to install dependencies
56
install:

CHANGES.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
1-
v0.1.0, 28 March, 2014 -- Initial release.
1+
Version 0.2.0 (2014-xx-xx)
2+
--------------------------
3+
Fixed Pycontracts dependency (#63)
4+
Made unrequired tracker method arguments optional (#40)
5+
Linked the Technical Docs and Setup Guide images to the appropriate pages (#60)
6+
Changed API to no longer specify a collector URL option (#57)
7+
Removed the "URL from Cloudfront subdomain" option (#56)
8+
Started sending event vendor parameter through on querystring (#55)
9+
Changed track screen view to use an unstructured event (#53)
10+
Added ability to name individual instances of the Python Tracker (#52)
11+
Validated GET payload in all integration tests (#33)
12+
Added support for Python 2.7 (#42)
13+
Fixed versions in requirements.txt (#47)
14+
Added platform and tracker version to payload (#50)
15+
Changed tracker version prefix from "python-" to "py-" (#51)
16+
17+
Version 0.1.0 (2014-28-03)
18+
--------------------------
19+
Initial release

README.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Find out more
2323
+---------------------------------+---------------------------+-------------------------+-----------------------------------+
2424
| Technical Docs | Setup Guide | Roadmap | Contributing |
2525
+=================================+===========================+=========================+===================================+
26-
| |techdocs| | |setup| | |roadmap| | |contributing| |
26+
| |techdocs|_ | |setup|_ | |roadmap| | |contributing| |
2727
+---------------------------------+---------------------------+-------------------------+-----------------------------------+
2828
| `Technical Docs`_ | `Setup Guide`_ | `Roadmap`_ | `Contributing`_ |
2929
+---------------------------------+---------------------------+-------------------------+-----------------------------------+
@@ -33,6 +33,8 @@ Find out more
3333
.. |roadmap| image:: https://d3i6fms1cm1j0i.cloudfront.net/github/images/roadmap.png
3434
.. |contributing| image:: https://d3i6fms1cm1j0i.cloudfront.net/github/images/contributing.png
3535

36+
.. _techdocs: https://github.com/snowplow/snowplow/wiki/Python-Tracker
37+
.. _setup: https://github.com/snowplow/snowplow/wiki/Python-Tracker-Setup
3638

3739
.. _`Technical Docs`: https://github.com/snowplow/snowplow/wiki/Python-Tracker
3840
.. _`Setup Guide`: https://github.com/snowplow/snowplow/wiki/Python-Tracker-Setup

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
pycontracts
2-
requests
1+
pycontracts==1.6.0
2+
requests==2.2.1

run-tests.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
cd $(dirname $0)
66

77
# pytest because it has a neat output
8-
/vagrant/snowplow-python-tracker-environment/bin/python3.3 -m pytest -s
8+
9+
/vagrant/snowplow-python-3.3-tracker-environment/bin/python3.3 -m pytest -s
10+
11+
/vagrant/snowplow-python-2.7-tracker-environment/bin/python2.7 -m pytest -s

setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
express or implied. See the Apache License Version 2.0 for the specific
1515
language governing permissions and limitations there under.
1616
17-
Authors: Anuj More, Alex Dean
17+
Authors: Anuj More, Alex Dean, Fred Blundun
1818
Copyright: Copyright (c) 2013-2014 Snowplow Analytics Ltd
1919
License: Apache License Version 2.0
2020
"""
@@ -40,6 +40,7 @@
4040
authors_list = [
4141
'Anuj More',
4242
'Alexander Dean',
43+
'Fred Blundun'
4344
]
4445
authors_str = ', '.join(authors_list)
4546

@@ -61,6 +62,6 @@
6162

6263
install_requires = [
6364
"requests",
64-
"pycontract",
65+
"pycontracts",
6566
],
6667
)

snowplow_tracker/_version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
express or implied. See the Apache License Version 2.0 for the specific
1515
language governing permissions and limitations there under.
1616
17-
Authors: Anuj More, Alex Dean
17+
Authors: Anuj More, Alex Dean, Fred Blundun
1818
Copyright: Copyright (c) 2013-2014 Snowplow Analytics Ltd
1919
License: Apache License Version 2.0
2020
"""
2121

2222

23-
__version_info__ = (0, 1, 0)
23+
__version_info__ = (0, 2, 0)
2424
__version__ = ".".join(str(x) for x in __version_info__)

snowplow_tracker/payload.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
express or implied. See the Apache License Version 2.0 for the specific
1515
language governing permissions and limitations there under.
1616
17-
Authors: Anuj More, Alex Dean
17+
Authors: Anuj More, Alex Dean, Fred Blundun
1818
Copyright: Copyright (c) 2013-2014 Snowplow Analytics Ltd
1919
License: Apache License Version 2.0
2020
"""
@@ -36,7 +36,7 @@ def __init__(self, tstamp=None, dict_=None):
3636

3737
self.context = {}
3838
# Set transaction for every event
39-
self.context['tid'] = Payload.set_transaction_id()
39+
self.context["tid"] = Payload.set_transaction_id()
4040
# Set timestamp for every event
4141
self.set_timestamp(tstamp)
4242
if dict_ is not None:
@@ -46,7 +46,7 @@ def __init__(self, tstamp=None, dict_=None):
4646
"""
4747
Special payload creation functions
4848
"""
49-
49+
@staticmethod
5050
def set_transaction_id():
5151
"""
5252
Set transaction ID for the payload once during the lifetime of the
@@ -67,7 +67,7 @@ def set_timestamp(self, tstamp=None):
6767
value = int(tstamp * 1000)
6868
else:
6969
value = tstamp
70-
self.context['dtm'] = value
70+
self.context["dtm"] = value
7171

7272
"""
7373
Payload creators
@@ -77,7 +77,8 @@ def add(self, name, value):
7777
"""
7878
Add a name value pair to the Payload object
7979
"""
80-
self.context[name] = value
80+
if not (value == "" or value is None):
81+
self.context[name] = value
8182

8283
@contract
8384
def add_dict(self, dict_, base64=False):
@@ -88,7 +89,7 @@ def add_dict(self, dict_, base64=False):
8889
:type dict_: dict(*:*)
8990
"""
9091
for f in dict_:
91-
self.context[f] = dict_[f]
92+
self.add(f, dict_[f])
9293

9394
@contract
9495
def add_unstruct(self, dict_, encode_base64,
@@ -109,7 +110,7 @@ def add_unstruct(self, dict_, encode_base64,
109110
:type type_when_not_encoded: str
110111
"""
111112
def raise_error(f, type_):
112-
raise RuntimeError(''.join([f, " in dict is not a ", type_]))
113+
raise RuntimeError("".join([f, " in dict is not a ", type_]))
113114

114115
types = ["int", "flt", "geo", "dt", "ts", "tms"]
115116

@@ -127,7 +128,7 @@ def raise_error(f, type_):
127128
json_dict = json.dumps(dict_)
128129

129130
if encode_base64:
130-
self.add(type_when_encoded, base64.urlsafe_b64encode(json_dict.encode('ascii')))
131+
self.add(type_when_encoded, base64.urlsafe_b64encode(json_dict.encode("ascii")))
131132
else:
132133
self.add(type_when_not_encoded, json_dict)
133134

Lines changed: 77 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
'''
1+
"""
22
test_integration.py
33
44
Copyright (c) 2013-2014 Snowplow Analytics Ltd. All rights reserved.
@@ -10,98 +10,134 @@
1010
1111
Unless required by applicable law or agreed to in writing,
1212
software distributed under the Apache License Version 2.0 is distributed on
13-
an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
1414
express or implied. See the Apache License Version 2.0 for the specific
1515
language governing permissions and limitations there under.
1616
17-
Authors: Anuj More, Alex Dean
17+
Authors: Anuj More, Alex Dean, Fred Blundun
1818
Copyright: Copyright (c) 2013-2014 Snowplow Analytics Ltd
1919
License: Apache License Version 2.0
20-
'''
20+
"""
2121

2222
import unittest
2323
import time
24-
from snowplow_tracker import tracker
24+
import re
25+
from snowplow_tracker import tracker, _version
2526
from httmock import all_requests, HTTMock
2627

28+
def from_querystring(field, url):
29+
pattern = re.compile("^[^#]*[?&]" + field + "=([^&#]*)")
30+
match = pattern.match(url)
31+
if match:
32+
return match.groups()[0]
2733

2834
@all_requests
2935
def pass_response_content(url, request):
30-
return 'True'
31-
36+
return {
37+
"url": request.url,
38+
"status_code": 200
39+
}
3240

3341
@all_requests
3442
def fail_response_content(url, request):
35-
return 'HTTP status code [501] is a server error'
43+
return "HTTP status code [501] is a server error"
3644

3745

3846
class IntegrationTest(unittest.TestCase):
3947

4048
def test_integration_page_view(self):
41-
t = tracker.Tracker.hostname('localhost')
49+
t = tracker.Tracker("localhost")
4250
with HTTMock(pass_response_content):
43-
val = t.track_page_view('http://savethearctic.org', 'Save The Arctic', None)
44-
self.assertTrue(val)
51+
val = t.track_page_view("http://savethearctic.org", "Save The Arctic", None)
52+
self.assertEquals(from_querystring("page", val), "Save+The+Arctic")
4553

4654
def test_integration_ecommerce_transaction(self):
47-
t = tracker.Tracker.hostname('localhost')
55+
t = tracker.Tracker("localhost")
4856
with HTTMock(pass_response_content):
49-
val = t.track_ecommerce_transaction('12345', 'Web', 9.99, 1.98, 3.00, 'London', 'Denver', 'Greenland')
50-
self.assertTrue(val)
57+
val = t.track_ecommerce_transaction("12345", 9.99, "Web", 1.98, 3.05, "London", "Denver", "Greenland")
58+
assertion_array = {"tr_tt": "9.99", "e": "tr", "tr_id": "12345", "tr_sh": "3.05", "tr_st": "Denver", "tr_af": "Web", "tr_co": "Greenland", "tr_tx": "1.98", "tr_ci": "London"}
59+
for key in assertion_array:
60+
self.assertEquals(from_querystring(key, val), assertion_array[key])
5161

5262
def test_integration_ecommerce_transaction_item(self):
53-
t = tracker.Tracker.hostname('localhost')
63+
t = tracker.Tracker("localhost")
5464
with HTTMock(pass_response_content):
55-
val = t.track_ecommerce_transaction_item('12345', 'pbz0025', 'black-tarot', 'tarot', 7.99, 2)
56-
self.assertTrue(val)
65+
val = t.track_ecommerce_transaction_item("12345", "pbz0025", 7.99, 2, "black-tarot", "tarot")
66+
assertion_array = {"ti_ca": "tarot", "ti_id": "12345", "ti_qu": "2", "ti_sk": "pbz0025", "e": "ti", "ti_nm": "black-tarot", "ti_pr": "7.99"}
67+
for key in assertion_array:
68+
self.assertEquals(from_querystring(key, val), assertion_array[key])
5769

5870
def test_integration_screen_view(self):
59-
t = tracker.Tracker.hostname('localhost')
71+
t = tracker.Tracker("localhost")
6072
with HTTMock(pass_response_content):
61-
val = t.track_screen_view('Game HUD 2', 'Hello!')
62-
self.assertTrue(val)
73+
val = t.track_screen_view("Game HUD 2", "Hello!")
74+
assertion_array = {"e": "ue", "ue_na": "screen_view"}
75+
for key in assertion_array:
76+
self.assertEquals(from_querystring(key, val), assertion_array[key])
6377

6478
def test_integration_struct_event(self):
65-
t = tracker.Tracker.hostname('localhost')
79+
t = tracker.Tracker("localhost")
6680
with HTTMock(pass_response_content):
67-
val = t.track_struct_event('Ecomm', 'add-to-basket', 'dog-skateboarding-video', 'hd', 13.99)
68-
self.assertTrue(val)
81+
val = t.track_struct_event("Ecomm", "add-to-basket", "dog-skateboarding-video", "hd", 13.99)
82+
assertion_array = {"se_ca": "Ecomm", "se_pr": "hd", "se_la": "dog-skateboarding-video", "se_va": "13.99", "se_ac": "add-to-basket", "e": "se"}
83+
for key in assertion_array:
84+
self.assertEquals(from_querystring(key, val), assertion_array[key])
85+
6986

7087
def test_integration_unstruct_event_non_base64(self):
71-
t = tracker.Tracker.hostname('localhost')
72-
t.config['encode_base64'] = False
88+
t = tracker.Tracker("localhost")
89+
t.config["encode_base64"] = False
7390
with HTTMock(pass_response_content):
74-
val = t.track_unstruct_event('viewed_product', {'product_id': 'ASO01043', 'price$flt': 49.95, 'walrus$tms': int(time.time() * 1000),})
75-
self.assertTrue(val)
91+
val = t.track_unstruct_event("viewed_product", {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": int(time.time() * 1000)})
92+
assertion_array = {"e": "ue", "ue_na": "viewed_product"}
93+
for key in assertion_array:
94+
self.assertEquals(from_querystring(key, val), assertion_array[key])
7695

7796
def test_integration_unstruct_event_base64(self):
78-
t = tracker.Tracker.hostname('localhost')
97+
t = tracker.Tracker("localhost")
7998
with HTTMock(pass_response_content):
80-
val = t.track_unstruct_event('viewed_product', {'product_id': 'ASO01043', 'price$flt': 49.95, 'walrus$tms': int(time.time() * 1000),})
81-
self.assertTrue(val)
99+
val = t.track_unstruct_event("viewed_product", {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": int(time.time() * 1000)})
100+
assertion_array = {"e": "ue", "ue_na": "viewed_product"}
101+
for key in assertion_array:
102+
self.assertEquals(from_querystring(key, val), assertion_array[key])
82103

83104
def test_integration_unstruct_event_non_base64_error(self):
84-
t = tracker.Tracker.hostname('localhost')
85-
t.config['encode_base64'] = False
105+
t = tracker.Tracker("localhost")
106+
t.config["encode_base64"] = False
86107
try:
87-
val = t.track_unstruct_event('viewed_product',
108+
val = t.track_unstruct_event("viewed_product",
88109
{
89-
'product_id': 'ASO01043',
90-
'price$flt': 49, # ERROR
91-
'walrus$tms': int(time.time() * 1000),
110+
"product_id": "ASO01043",
111+
"price$flt": 49, # ERROR
112+
"walrus$tms": int(time.time() * 1000),
92113
})
93114
except RuntimeError as e:
94115
self.assertEquals("price$flt in dict is not a flt", str(e))
95116

96117

97118
def test_integration_unstruct_event_base64_error(self):
98-
t = tracker.Tracker.hostname('localhost')
119+
t = tracker.Tracker("localhost")
99120
try:
100-
val = t.track_unstruct_event('viewed_product',
121+
val = t.track_unstruct_event("viewed_product",
101122
{
102-
'product_id': 'ASO01043',
103-
'price$flt': 49.95,
104-
'walrus$tms': 'hello', # ERROR
123+
"product_id": "ASO01043",
124+
"price$flt": 49.95,
125+
"walrus$tms": "hello", # ERROR
105126
})
106127
except RuntimeError as e:
107128
self.assertEquals("walrus$tms in dict is not a tms", str(e))
129+
130+
def test_integration_standard_nv_pairs(self):
131+
t = tracker.Tracker("localhost", "cf")
132+
t.set_platform("mob")
133+
t.set_user_id("user12345")
134+
t.set_app_id("angry-birds-android")
135+
t.set_screen_resolution(100, 200)
136+
t.set_color_depth(24)
137+
t.set_timezone("Europe London")
138+
t.set_lang("en")
139+
with HTTMock(pass_response_content):
140+
val = t.track_page_view("localhost", "local host", None)
141+
assertion_array = {"tna": "cf", "evn": "com.snowplowanalytics", "res": "100x200", "lang": "en", "aid": "angry-birds-android", "cd": "24", "tz": "Europe+London", "p": "mob", "tv": "py-" + _version.__version__}
142+
for key in assertion_array:
143+
self.assertEquals(from_querystring(key, val), assertion_array[key])
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)