Skip to content

Commit 033c780

Browse files
committed
Merge branch 'release/0.15.0'
2 parents 5d33568 + c894d4d commit 033c780

16 files changed

Lines changed: 241 additions & 6 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
- name: Demo
5757
run: |
5858
cd examples
59+
cd tracker_api_example
5960
python app.py "localhost:9090"
6061
6162
- name: Coveralls

CHANGES.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
Version 0.15.0 (2023-04-19)
2+
---------------------------
3+
Use Requests Session for sending eventss (#221)
4+
Add Redis example app (#322)
5+
16
Version 0.14.0 (2023-03-21)
27
---------------------------
38
Adds deprecation warnings for V1 changes (#315)

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
author = 'Alex Dean, Paul Boocock, Matus Tomlein, Jack Keene'
2929

3030
# The full version, including alpha/beta/rc tags
31-
release = "0.14"
31+
release = "0.15"
3232

3333

3434
# -- General configuration ---------------------------------------------------

examples/redis_example/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Redis Example App
2+
3+
This example shows how to set up the Python tracker with a Redis database and a Redis worker to forward events to a Snowplow pipeline.
4+
5+
#### Installation
6+
- Install the Python tracker from the root folder of the project.
7+
8+
`python setup.py install`
9+
10+
- Install redis for your machine. More information can be found [here](https://redis.io/docs/getting-started/installation/)
11+
12+
`brew install redis`
13+
14+
- Run `redis-server` to check your redis installation, to stop the server enter `ctrl+c`.
15+
16+
#### Usage
17+
Navigate to the example folder.
18+
19+
`cd examples/redis_example`
20+
21+
This example has two programmes, `redis_app.py` tracks events and sends them to a redis database, `redis_worker.py` then forwards these events onto a Snowplow pipeline.
22+
23+
To send events to your pipeline, run `redis-server`, followed by the `redis_worker.py {{your_collector_endpoint}}` and finally `redis_app.py`. You should see 3 events in your pipleine.
24+
25+
26+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from snowplow_tracker import Tracker
2+
from snowplow_tracker.typing import PayloadDict
3+
import json
4+
import redis
5+
import logging
6+
7+
# logging
8+
logging.basicConfig()
9+
logger = logging.getLogger(__name__)
10+
logger.setLevel(logging.INFO)
11+
12+
13+
class RedisEmitter(object):
14+
"""
15+
Sends Snowplow events to a Redis database
16+
"""
17+
18+
def __init__(self, rdb=None, key: str = "redis_key") -> None:
19+
"""
20+
:param rdb: Optional custom Redis database
21+
:type rdb: redis | None
22+
:param key: The Redis key for the list of events
23+
:type key: string
24+
"""
25+
26+
if rdb is None:
27+
rdb = redis.StrictRedis()
28+
29+
self.rdb = rdb
30+
self.key = key
31+
32+
def input(self, payload: PayloadDict) -> None:
33+
"""
34+
:param payload: The event properties
35+
:type payload: dict(string:*)
36+
"""
37+
logger.info("Pushing event to Redis queue...")
38+
self.rdb.rpush(self.key, json.dumps(payload))
39+
logger.info("Finished sending event to Redis.")
40+
41+
def flush(self) -> None:
42+
logger.warning("The RedisEmitter class does not need to be flushed")
43+
return
44+
45+
def sync_flush(self) -> None:
46+
self.flush()
47+
48+
49+
def main():
50+
emitter = RedisEmitter()
51+
52+
t = Tracker(emitter)
53+
54+
t.track_page_view("https://www.snowplow.io", "Homepage")
55+
t.track_page_ping("https://www.snowplow.io", "Homepage")
56+
t.track_link_click("https://www.snowplow.io")
57+
58+
59+
if __name__ == "__main__":
60+
main()
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import sys
2+
from snowplow_tracker import Emitter
3+
from typing import Any
4+
from snowplow_tracker.typing import PayloadDict
5+
import json
6+
import redis
7+
import signal
8+
import gevent
9+
from gevent.pool import Pool
10+
11+
12+
def get_url_from_args():
13+
if len(sys.argv) != 2:
14+
raise ValueError("Collector Endpoint is required")
15+
return sys.argv[1]
16+
17+
18+
class RedisWorker:
19+
def __init__(self, emitter: Emitter, key) -> None:
20+
self.pool = Pool(5)
21+
self.emitter = emitter
22+
self.rdb = redis.StrictRedis()
23+
self.key = key
24+
25+
signal.signal(signal.SIGTERM, self.request_shutdown)
26+
signal.signal(signal.SIGINT, self.request_shutdown)
27+
signal.signal(signal.SIGQUIT, self.request_shutdown)
28+
29+
def send(self, payload: PayloadDict) -> None:
30+
"""
31+
Send an event to an emitter
32+
"""
33+
self.emitter.input(payload)
34+
35+
def pop_payload(self) -> None:
36+
"""
37+
Get a single event from Redis and send it
38+
If the Redis queue is empty, sleep to avoid making continual requests
39+
"""
40+
payload = self.rdb.lpop(self.key)
41+
if payload:
42+
self.pool.spawn(self.send, json.loads(payload.decode("utf-8")))
43+
else:
44+
gevent.sleep(5)
45+
46+
def run(self) -> None:
47+
"""
48+
Run indefinitely
49+
"""
50+
self._shutdown = False
51+
while not self._shutdown:
52+
self.pop_payload()
53+
self.pool.join(timeout=20)
54+
55+
def request_shutdown(self, *args: Any) -> None:
56+
"""
57+
Halt the worker
58+
"""
59+
self._shutdown = True
60+
61+
62+
def main():
63+
collector_url = get_url_from_args()
64+
65+
# Configure Emitter
66+
emitter = Emitter(collector_url, batch_size=1)
67+
68+
# Setup worker
69+
worker = RedisWorker(emitter=emitter, key="redis_key")
70+
worker.run()
71+
72+
73+
if __name__ == "__main__":
74+
main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
redis~=4.5
2+
gevent~=22.10
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Snowplow API Example App
2+
3+
This example shows how to set up the Python tracker with the Snowplow API to send events to a Snowplow pipeline.
4+
5+
#### Installation
6+
- Install the Python tracker from the root folder of the project.
7+
8+
`python setup.py install`
9+
10+
#### Usage
11+
Navigate to the example folder.
12+
13+
`cd examples/snowplow_api_example`
14+
15+
To send events to your pipeline, run `snowplow_app.py {{your_collector_endpoint}}`. You should see 6 events in your pipleine.
16+
17+
18+

examples/snowplow_app.py renamed to examples/snowplow_api_example/snowplow_app.py

File renamed without changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Example App
2+
3+
This example shows how to set up the Python tracker with the tracker API to send events to a Snowplow pipeline.
4+
5+
#### Installation
6+
- Install the Python tracker from the root folder of the project.
7+
8+
`python setup.py install`
9+
10+
#### Usage
11+
Navigate to the example folder.
12+
13+
`cd examples/tracker_api_example`
14+
15+
To send events to your pipeline, run `app.py {{your_collector_endpoint}}`. You should see 5 events in your pipleine.
16+
17+
18+

0 commit comments

Comments
 (0)