Compare commits

...
Author SHA1 Message Date
Tim Glaser 8977c4e3ab Merge branch 'add-tests' of github.com:PostHog/posthog-python into add-tests 2020-09-11 15:37:31 +02:00
Tim Glaser 710ac05862 Cleaned up simulator 2020-09-11 15:37:06 +02:00
Yakko Majuri 678e4ac97b minor changes 2020-09-11 08:50:50 +00:00
Yakko Majuri 6508aa6994 further cleanup of action 2020-09-09 16:48:07 +00:00
Yakko Majuri 3b7f37aa39 updated tests action 2020-09-09 16:44:41 +00:00
Tim Glaser ac74ee9a5c fix test 2020-09-09 16:26:59 +02:00
Tim Glaser b9e323bf47 Fix tests 2020-09-09 16:22:40 +02:00
Tim Glaser 8fee12f004 Move tests into correct folder 2020-09-09 16:16:54 +02:00
Tim Glaser 3c23f0159f Fix tests 2020-09-09 16:15:46 +02:00
Marius AndraandGitHub c944d7df2c Update README.md 2020-05-20 16:43:32 +02:00
Tim Glaser 611e01f9eb Release 1.0.11 2020-04-30 10:10:51 +01:00
Tim GlaserandGitHub 1840bb8f57 Merge pull request #6 from PostHog/5-timestamp-correct-type
Closes #5, add correct timestamp types
2020-04-30 10:08:51 +01:00
Tim Glaser cb06d2fd5d Closes #5, add correct timestamp types 2020-04-30 10:08:28 +01:00
Marius AndraandGitHub 610fc816f1 Merge pull request #3 from casio/patch-1
Fix typo in identify() docstring
2020-04-29 22:12:44 +02:00
Marius AndraandGitHub 992ab2b9a7 Merge pull request #4 from PostHog/links-fix
Fixed links to docs
2020-04-29 22:12:05 +02:00
James HawkinsandGitHub fd68a10e63 Fixed links to docs 2020-04-07 16:26:06 +01:00
Tim Glaser 48c1cf3c02 Update instructions 2020-04-01 17:26:10 +01:00
Tim Glaser b923116a9e Release 1.0.10 2020-04-01 17:25:23 +01:00
Carsten KrausandGitHub 0eed53222c Fix typo in docstring
lil typo, fixed : )
2020-03-25 22:42:05 +01:00
Tim Glaser 1670901156 Merge branch 'master' of github.com:PostHog/posthog-python 2020-02-21 17:09:32 -08:00
Tim Glaser ab65f8f0f8 Add 'disabled' option for use in tests 2020-02-21 17:08:49 -08:00
Tim GlaserandGitHub 133e1a991c Merge pull request #1 from mariusandra/patch-1
Fix typo with "identify" code example
2020-02-18 18:19:25 -08:00
Marius AndraandGitHub 7a93a99541 Fix typo with "identify" code example 2020-02-18 22:10:02 +01:00
13 changed files with 158 additions and 387 deletions
+27
View File
@@ -0,0 +1,27 @@
name: Backend CI
on:
- pull_request
jobs:
tests:
name: Python tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Set up Python 3.7
uses: actions/setup-python@v1
with:
python-version: 3.7
- name: Install requirements.txt dependencies with pip
run: |
python -m pip install -e .
- name: Run posthog tests
run: |
python setup.py test
+2 -117
View File
@@ -1,120 +1,5 @@
# PostHog Python
Official PostHog Python library to capture and send events to any PostHog instance (including PostHog.com).
Please see the main [PostHog docs](https://posthog.com/docs).
This library uses an internal queue to make calls non-blocking and fast. It also batches requests and flushes asynchronously, making it perfect to use in any part of your web app or other server side application that needs performance.
## Installation
```bash
pip install posthog
```
In your app, import the posthog library and set your api key **before** making any calls.
```python
import posthog
posthog.api_key = 'YOUR API KEY'
```
You can find your key in the /setup page in PostHog.
To debug, you can set debug mode.
```python
posthog.debug = True
```
## Making calls
### Capture
Capture allows you to capture anything a user does within your system, which you can later use in PostHog to find patterns in usage, work out which features to improve or where people are giving up.
A `capture` call requires
- `distinct id` which uniquely identifies your user
- `event name` to make sure
- We recommend using [verb] [noun], like `movie played` or `movie updated` to easily identify what your events mean later on.
Optionally you can submit
- `properties`, which can be a dict with any information you'd like to add
For example:
```python
posthog.capture('distinct id', 'movie played', {'movie_id': '123', 'category': 'romcom'})
```
### Identify
Identify lets you add metadata on your users so you can more easily identify who they are in PostHog, and even do things like segment users by these properties.
An `identify` call requires
- `distinct id` which uniquely identifies your user
- `properties` with a dict with any key: value pairs
For example:
```python
posthog.identify('distinct id', {
'email': 'dwayne@gmail.com',
'name': 'Dwayne Johnson'
})
```
The most obvious place to make this call is whenever a user signs up, or when they update their information.
### Alias
To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call. This will allow you to answer questions like "Which marketing channels leads to users churning after a month?" or "What do users do on our website before signing up?"
In a purely back-end implementation, this means whenever an anonymous user does something, you'll want to send a session ID ([Django](https://stackoverflow.com/questions/526179/in-django-how-can-i-find-out-the-request-session-sessionid-and-use-it-as-a-vari), [Flask](https://stackoverflow.com/questions/15156132/flask-login-how-to-get-session-id)) with the capture call. Then, when that users signs up, you want to do an alias call with the session ID and the newly created user ID.
The same concept applies for when a user logs in.
If you're using PostHog in the front-end and back-end, doing the `identify` call in the frontend will be enough.
An `alias` call requires
- `previous distinct id` the unique ID of the user before
- `distinct id` the current unique id
For example:
```python
posthog.alias('anonymous session id', 'distinct id')
```
## Django
For Django, you can do the initialisation of the key in the AppConfig, so that it's available everywhere.
in `yourapp/apps.py`
```python
from django.apps import AppConfig
import posthog
class YourAppConfig(AppConfig):
def ready(self):
posthog.api_key = 'your key'
```
Then, anywhere else in your app you can do
```python
import posthog
def homepage(request):
# example capture
posthog.capture(request.session.session_key, 'page view', ....)
```
# Development
## Naming confusion
As our open source project [PostHog](https://github.com/PostHog/posthog) shares the same module name, we create a special `posthog-analytics` package, mostly for internal use to avoid module collision. It is the exact same.
## How to release
1. Increase `VERSION` in `posthog/version.py`
2. run `make release` and `make release_analytics`
3. `git commit -am "Release X.Y.Z."` (where X.Y.Z is the new version)
4. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version).
## Thank you
This library is largely based on the `analytics-python` package.
Specifically, the [Python integration](https://posthog.com/docs/integrations/python-integration) details.
+5 -5
View File
@@ -8,13 +8,13 @@ posthog.api_key = '<your key>'
# Where you host PostHog, with no trailing /.
# You can remove this line if you're using posthog.com
posthog.host = 'http://127.0.0.1:8000'
# posthog.host = 'http://127.0.0.1:8000'
# Capture an event
posthog.capture('distinct_id', 'event', {'property1': 'value', 'property2': 'value'})
# Alias a previous distinct id with a new one
posthog.alias('distinct_id', 'new_distinct_id')
# # Alias a previous distinct id with a new one
# posthog.alias('distinct_id', 'new_distinct_id')
# Add properties to the person
posthog.identify('distinct_id', {'email': 'something@something.com'})
# # Add properties to the person
# posthog.identify('distinct_id', {'email': 'something@something.com'})
+35 -12
View File
@@ -6,18 +6,26 @@ from typing import Optional, Dict, Callable
__version__ = VERSION
"""Settings."""
api_key: str = None
host: str = None
on_error: Callable = None
debug: bool = False
send: bool = True
sync_mode:bool = False
api_key = None # type: str
host = None # type: str
on_error = None # type: Callable
debug = False # type: bool
send = True # type: bool
sync_mode = False # type: bool
disabled = False # type: bool
default_client = None
def capture(distinct_id: str, event: str, properties: Optional[Dict]=None, context: Optional[Dict]=None,
timestamp: Optional[str]=None, message_id: Optional[str]=None) -> None:
def capture(
distinct_id, # type: str,
event, # type: str,
properties=None, # type: Optional[Dict]
context=None, # type: Optional[Dict]
timestamp=None, # type: Optional[datetime.datetime]
message_id=None, # type: Optional[str]
):
# type: (...) -> None
"""
Capture allows you to capture anything a user does within your system, which you can later use in PostHog to find patterns in usage, work out which features to improve or where people are giving up.
@@ -36,8 +44,14 @@ def capture(distinct_id: str, event: str, properties: Optional[Dict]=None, conte
"""
_proxy('capture', distinct_id=distinct_id, event=event, properties=properties, context=context, timestamp=timestamp, message_id=message_id)
def identify(distinct_id: str, properties: Optional[Dict]=None, context: Optional[Dict]=None, timestamp: Optional[str]=None,
message_id=None) -> None:
def identify(
distinct_id, # type: str,
properties=None, # type: Optional[Dict]
context=None, # type: Optional[Dict]
timestamp=None, # type: Optional[datetime.datetime]
message_id=None, # type: Optional[str]
):
# type: (...) -> None
"""
Identify lets you add metadata on your users so you can more easily identify who they are in PostHog, and even do things like segment users by these properties.
@@ -47,7 +61,7 @@ def identify(distinct_id: str, properties: Optional[Dict]=None, context: Optiona
For example:
```python
posthog.capture('distinct id', {
posthog.identify('distinct id', {
'email': 'dwayne@gmail.com',
'name': 'Dwayne Johnson'
})
@@ -60,7 +74,14 @@ def group(*args, **kwargs):
_proxy('group', *args, **kwargs)
def alias(previous_id: str, distinct_id: str, context: Optional[Dict]=None, timestamp: Optional[str]=None, message_id: Optional[str]=None) -> None:
def alias(
previous_id, # type: str,
distinct_id, # type: str,
context=None, # type: Optional[Dict]
timestamp=None, # type: Optional[datetime.datetime]
message_id=None, # type: Optional[str]
):
# type: (...) -> None
"""
To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call. This will allow you to answer questions like "Which marketing channels leads to users churning after a month?" or "What do users do on our website before signing up?"
@@ -109,6 +130,8 @@ def shutdown():
def _proxy(method, *args, **kwargs):
"""Create an analytics client if one doesn't exist and send to it."""
global default_client
if disabled:
return None
if not default_client:
default_client = Client(api_key, host=host, debug=debug,
on_error=on_error, send=send,
+6 -62
View File
@@ -78,7 +78,6 @@ class Client(object):
msg = {
'timestamp': timestamp,
'context': context,
'type': 'identify',
'distinct_id': distinct_id,
'$set': properties,
'event': '$identify',
@@ -100,7 +99,6 @@ class Client(object):
'timestamp': timestamp,
'context': context,
'distinct_id': distinct_id,
'type': 'capture',
'event': event,
'messageId': message_id,
}
@@ -121,34 +119,12 @@ class Client(object):
},
'timestamp': timestamp,
'context': context,
'type': 'alias',
'event': '$create_alias'
}
return self._enqueue(msg)
def group(self, distinct_id=None, group_id=None, traits=None, context=None,
timestamp=None, message_id=None):
traits = traits or {}
context = context or {}
require('distinct_id', distinct_id, ID_TYPES)
require('group_id', group_id, ID_TYPES)
require('traits', traits, dict)
msg = {
'timestamp': timestamp,
'groupId': group_id,
'context': context,
'distinct_id': distinct_id,
'traits': traits,
'type': 'group',
'messageId': message_id,
}
return self._enqueue(msg)
def page(self, distinct_id=None, category=None, name=None, properties=None,
def page(self, distinct_id=None, url=None, properties=None,
context=None, timestamp=None, message_id=None):
properties = properties or {}
context = context or {}
@@ -156,46 +132,15 @@ class Client(object):
require('distinct_id', distinct_id, ID_TYPES)
require('properties', properties, dict)
if name:
require('name', name, string_types)
if category:
require('category', category, string_types)
require('url', url, string_types)
properties['$current_url'] = url
msg = {
'event': '$pageview',
'properties': properties,
'timestamp': timestamp,
'category': category,
'context': context,
'distinct_id': distinct_id,
'type': 'page',
'name': name,
'messageId': message_id,
}
return self._enqueue(msg)
def screen(self, distinct_id=None, category=None, name=None, properties=None,
context=None, timestamp=None, message_id=None):
properties = properties or {}
context = context or {}
require('distinct_id', distinct_id, ID_TYPES)
require('properties', properties, dict)
if name:
require('name', name, string_types)
if category:
require('category', category, string_types)
msg = {
'properties': properties,
'timestamp': timestamp,
'category': category,
'context': context,
'distinct_id': distinct_id,
'type': 'screen',
'name': name,
'messageId': message_id,
}
@@ -210,7 +155,6 @@ class Client(object):
if message_id is None:
message_id = uuid4()
require('type', msg['type'], string_types)
require('timestamp', timestamp, datetime)
require('context', msg['context'], dict)
@@ -233,7 +177,7 @@ class Client(object):
return True, msg
if self.sync_mode:
self.log.debug('enqueued with blocking %s.', msg['type'])
self.log.debug('enqueued with blocking %s.', msg['event'])
post(self.api_key, self.host, gzip=self.gzip,
timeout=self.timeout, batch=[msg])
@@ -241,7 +185,7 @@ class Client(object):
try:
self.queue.put(msg, block=False)
self.log.debug('enqueued %s.', msg['type'])
self.log.debug('enqueued %s.', msg['event'])
return True, msg
except queue.Full:
self.log.warning('analytics-python queue is full')
+1 -1
View File
@@ -6,7 +6,7 @@ import sys
def all_names():
for _, modname, _ in pkgutil.iter_modules(__path__):
yield 'analytics.test.' + modname
yield 'posthog.test.' + modname
def all():
+32 -123
View File
@@ -6,17 +6,18 @@ import time
from posthog.version import VERSION
from posthog.client import Client
from posthog.test.utils import TEST_API_KEY
class TestClient(unittest.TestCase):
def fail(self, e, batch):
"""Mark the failure handler"""
print('FAIL', e, batch)
self.failed = True
def setUp(self):
self.failed = False
self.client = Client('testsecret', on_error=self.fail)
self.client = Client(TEST_API_KEY, on_error=self.fail)
def test_requires_api_key(self):
self.assertRaises(AssertionError, Client)
@@ -24,9 +25,9 @@ class TestClient(unittest.TestCase):
def test_empty_flush(self):
self.client.flush()
def test_basic_track(self):
def test_basic_capture(self):
client = self.client
success, msg = client.track('distinct_id', 'python test event')
success, msg = client.capture('distinct_id', 'python test event')
client.flush()
self.assertTrue(success)
self.assertFalse(self.failed)
@@ -35,14 +36,14 @@ class TestClient(unittest.TestCase):
self.assertTrue(isinstance(msg['timestamp'], str))
self.assertTrue(isinstance(msg['messageId'], str))
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['properties'], {})
self.assertEqual(msg['type'], 'track')
self.assertEqual(msg['properties']['$lib'], 'posthog-python')
self.assertEqual(msg['properties']['$lib_version'], VERSION)
def test_stringifies_distinct_id(self):
# A large number that loses precision in node:
# node -e "console.log(157963456373623802 + 1)" > 157963456373623800
client = self.client
success, msg = client.track(
success, msg = client.capture(
distinct_id=157963456373623802, event='python test event')
client.flush()
self.assertTrue(success)
@@ -50,9 +51,9 @@ class TestClient(unittest.TestCase):
self.assertEqual(msg['distinct_id'], '157963456373623802')
def test_advanced_track(self):
def test_advanced_capture(self):
client = self.client
success, msg = client.track(
success, msg = client.capture(
'distinct_id', 'python test event', {'property': 'value'},
{'ip': '192.168.0.1'}, datetime(2014, 9, 3),
'messageId')
@@ -60,14 +61,13 @@ class TestClient(unittest.TestCase):
self.assertTrue(success)
self.assertEqual(msg['timestamp'], '2014-09-03T00:00:00+00:00')
self.assertEqual(msg['properties'], {'property': 'value'})
self.assertEqual(msg['properties']['property'], 'value')
self.assertEqual(msg['context']['ip'], '192.168.0.1')
self.assertEqual(msg['event'], 'python test event')
self.assertEqual(msg['properties']['$lib'], 'posthog-python')
self.assertEqual(msg['properties']['$lib_version'], VERSION)
self.assertEqual(msg['messageId'], 'messageId')
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'track')
def test_basic_identify(self):
client = self.client
@@ -76,11 +76,10 @@ class TestClient(unittest.TestCase):
self.assertTrue(success)
self.assertFalse(self.failed)
self.assertEqual(msg['traits'], {'trait': 'value'})
self.assertEqual(msg['$set']['trait'], 'value')
self.assertTrue(isinstance(msg['timestamp'], str))
self.assertTrue(isinstance(msg['messageId'], str))
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'identify')
def test_advanced_identify(self):
client = self.client
@@ -92,46 +91,12 @@ class TestClient(unittest.TestCase):
self.assertEqual(msg['timestamp'], '2014-09-03T00:00:00+00:00')
self.assertEqual(msg['context']['ip'], '192.168.0.1')
self.assertEqual(msg['traits'], {'trait': 'value'})
self.assertEqual(msg['context']['library'], {
'name': 'analytics-python',
'version': VERSION
})
self.assertEqual(msg['$set']['trait'], 'value')
self.assertEqual(msg['properties']['$lib'], 'posthog-python')
self.assertEqual(msg['properties']['$lib_version'], VERSION)
self.assertTrue(isinstance(msg['timestamp'], str))
self.assertEqual(msg['messageId'], 'messageId')
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'identify')
def test_basic_group(self):
client = self.client
success, msg = client.group('distinct_id', 'groupId')
client.flush()
self.assertTrue(success)
self.assertFalse(self.failed)
self.assertEqual(msg['groupId'], 'groupId')
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'group')
def test_advanced_group(self):
client = self.client
success, msg = client.group(
'distinct_id', 'groupId', {'trait': 'value'}, {'ip': '192.168.0.1'},
datetime(2014, 9, 3), 'messageId')
self.assertTrue(success)
self.assertEqual(msg['timestamp'], '2014-09-03T00:00:00+00:00')
self.assertEqual(msg['context']['ip'], '192.168.0.1')
self.assertEqual(msg['traits'], {'trait': 'value'})
self.assertEqual(msg['context']['library'], {
'name': 'analytics-python',
'version': VERSION
})
self.assertTrue(isinstance(msg['timestamp'], str))
self.assertEqual(msg['messageId'], 'messageId')
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'group')
def test_basic_alias(self):
client = self.client
@@ -139,71 +104,35 @@ class TestClient(unittest.TestCase):
client.flush()
self.assertTrue(success)
self.assertFalse(self.failed)
self.assertEqual(msg['previousId'], 'previousId')
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['properties']['distinct_id'], 'previousId')
self.assertEqual(msg['properties']['alias'], 'distinct_id')
def test_basic_page(self):
client = self.client
success, msg = client.page('distinct_id', name='name')
success, msg = client.page('distinct_id', url='https://posthog.com/contact')
self.assertFalse(self.failed)
client.flush()
self.assertTrue(success)
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'page')
self.assertEqual(msg['name'], 'name')
self.assertEqual(msg['properties']['$current_url'], 'https://posthog.com/contact')
def test_advanced_page(self):
client = self.client
success, msg = client.page(
'distinct_id', 'category', 'name', {'property': 'value'},
'distinct_id', 'https://posthog.com/contact', {'property': 'value'},
{'ip': '192.168.0.1'}, datetime(2014, 9, 3), 'messageId')
self.assertTrue(success)
self.assertEqual(msg['timestamp'], '2014-09-03T00:00:00+00:00')
self.assertEqual(msg['context']['ip'], '192.168.0.1')
self.assertEqual(msg['properties'], {'property': 'value'})
self.assertEqual(msg['context']['library'], {
'name': 'analytics-python',
'version': VERSION
})
self.assertEqual(msg['category'], 'category')
self.assertEqual(msg['properties']['$current_url'], 'https://posthog.com/contact')
self.assertEqual(msg['properties']['property'], 'value')
self.assertEqual(msg['properties']['$lib'], 'posthog-python')
self.assertEqual(msg['properties']['$lib_version'], VERSION)
self.assertTrue(isinstance(msg['timestamp'], str))
self.assertEqual(msg['messageId'], 'messageId')
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'page')
self.assertEqual(msg['name'], 'name')
def test_basic_screen(self):
client = self.client
success, msg = client.screen('distinct_id', name='name')
client.flush()
self.assertTrue(success)
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'screen')
self.assertEqual(msg['name'], 'name')
def test_advanced_screen(self):
client = self.client
success, msg = client.screen(
'distinct_id', 'category', 'name', {'property': 'value'},
{'ip': '192.168.0.1'}, datetime(2014, 9, 3), 'messageId')
self.assertTrue(success)
self.assertEqual(msg['timestamp'], '2014-09-03T00:00:00+00:00')
self.assertEqual(msg['context']['ip'], '192.168.0.1')
self.assertEqual(msg['properties'], {'property': 'value'})
self.assertEqual(msg['context']['library'], {
'name': 'analytics-python',
'version': VERSION
})
self.assertTrue(isinstance(msg['timestamp'], str))
self.assertEqual(msg['messageId'], 'messageId')
self.assertEqual(msg['category'], 'category')
self.assertEqual(msg['distinct_id'], 'distinct_id')
self.assertEqual(msg['type'], 'screen')
self.assertEqual(msg['name'], 'name')
def test_flush(self):
client = self.client
@@ -230,7 +159,7 @@ class TestClient(unittest.TestCase):
self.assertFalse(consumer.is_alive())
def test_synchronous(self):
client = Client('testsecret', sync_mode=True)
client = Client(TEST_API_KEY, sync_mode=True)
success, message = client.identify('distinct_id')
self.assertFalse(client.consumers)
@@ -238,7 +167,7 @@ class TestClient(unittest.TestCase):
self.assertTrue(success)
def test_overflow(self):
client = Client('testsecret', max_queue_size=1)
client = Client(TEST_API_KEY, max_queue_size=1)
# Ensure consumer thread is no longer uploading
client.join()
@@ -249,46 +178,26 @@ class TestClient(unittest.TestCase):
# Make sure we are informed that the queue is at capacity
self.assertFalse(success)
def test_success_on_invalid_api_key(self):
client = Client('bad_key', on_error=self.fail)
client.track('distinct_id', 'event')
client.flush()
self.assertFalse(self.failed)
def test_unicode(self):
Client(six.u('unicode_key'))
def test_numeric_distinct_id(self):
self.client.track(1234, 'python event')
self.client.capture(1234, 'python event')
self.client.flush()
self.assertFalse(self.failed)
def test_debug(self):
Client('bad_key', debug=True)
def test_identify_with_date_object(self):
client = self.client
success, msg = client.identify(
'distinct_id',
{
'birthdate': date(1981, 2, 2),
},
)
client.flush()
self.assertTrue(success)
self.assertFalse(self.failed)
self.assertEqual(msg['traits'], {'birthdate': date(1981, 2, 2)})
def test_gzip(self):
client = Client('testsecret', on_error=self.fail, gzip=True)
client = Client(TEST_API_KEY, on_error=self.fail, gzip=True)
for _ in range(10):
client.identify('distinct_id', {'trait': 'value'})
client.flush()
self.assertFalse(self.failed)
def test_user_defined_flush_at(self):
client = Client('testsecret', on_error=self.fail,
client = Client(TEST_API_KEY, on_error=self.fail,
flush_at=10, flush_interval=3)
def mock_post_fn(*args, **kwargs):
@@ -296,7 +205,7 @@ class TestClient(unittest.TestCase):
# the post function should be called 2 times, with a batch size of 10
# each time.
with mock.patch('analytics.consumer.post', side_effect=mock_post_fn) \
with mock.patch('posthog.consumer.post', side_effect=mock_post_fn) \
as mock_post:
for _ in range(20):
client.identify('distinct_id', {'trait': 'value'})
@@ -304,11 +213,11 @@ class TestClient(unittest.TestCase):
self.assertEquals(mock_post.call_count, 2)
def test_user_defined_timeout(self):
client = Client('testsecret', timeout=10)
client = Client(TEST_API_KEY, timeout=10)
for consumer in client.consumers:
self.assertEquals(consumer.timeout, 10)
def test_default_timeout_15(self):
client = Client('testsecret')
client = Client(TEST_API_KEY)
for consumer in client.consumers:
self.assertEquals(consumer.timeout, 15)
+16 -15
View File
@@ -10,6 +10,7 @@ except ImportError:
from posthog.consumer import Consumer, MAX_MSG_SIZE
from posthog.request import APIError
from posthog.test.utils import TEST_API_KEY
class TestConsumer(unittest.TestCase):
@@ -41,7 +42,7 @@ class TestConsumer(unittest.TestCase):
def test_upload(self):
q = Queue()
consumer = Consumer(q, 'testsecret')
consumer = Consumer(q, TEST_API_KEY)
track = {
'type': 'track',
'event': 'python event',
@@ -57,9 +58,9 @@ class TestConsumer(unittest.TestCase):
# The consumer should upload _n_ times.
q = Queue()
flush_interval = 0.3
consumer = Consumer(q, 'testsecret', flush_at=10,
consumer = Consumer(q, TEST_API_KEY, flush_at=10,
flush_interval=flush_interval)
with mock.patch('analytics.consumer.post') as mock_post:
with mock.patch('posthog.consumer.post') as mock_post:
consumer.start()
for i in range(0, 3):
track = {
@@ -77,9 +78,9 @@ class TestConsumer(unittest.TestCase):
q = Queue()
flush_interval = 0.5
flush_at = 10
consumer = Consumer(q, 'testsecret', flush_at=flush_at,
consumer = Consumer(q, TEST_API_KEY, flush_at=flush_at,
flush_interval=flush_interval)
with mock.patch('analytics.consumer.post') as mock_post:
with mock.patch('posthog.consumer.post') as mock_post:
consumer.start()
for i in range(0, flush_at * 2):
track = {
@@ -92,7 +93,7 @@ class TestConsumer(unittest.TestCase):
self.assertEqual(mock_post.call_count, 2)
def test_request(self):
consumer = Consumer(None, 'testsecret')
consumer = Consumer(None, TEST_API_KEY)
track = {
'type': 'track',
'event': 'python event',
@@ -109,7 +110,7 @@ class TestConsumer(unittest.TestCase):
raise expected_exception
mock_post.call_count = 0
with mock.patch('analytics.consumer.post',
with mock.patch('posthog.consumer.post',
mock.Mock(side_effect=mock_post)):
track = {
'type': 'track',
@@ -135,21 +136,21 @@ class TestConsumer(unittest.TestCase):
def test_request_retry(self):
# we should retry on general errors
consumer = Consumer(None, 'testsecret')
consumer = Consumer(None, TEST_API_KEY)
self._test_request_retry(consumer, Exception('generic exception'), 2)
# we should retry on server errors
consumer = Consumer(None, 'testsecret')
consumer = Consumer(None, TEST_API_KEY)
self._test_request_retry(consumer, APIError(
500, 'code', 'Internal Server Error'), 2)
# we should retry on HTTP 429 errors
consumer = Consumer(None, 'testsecret')
consumer = Consumer(None, TEST_API_KEY)
self._test_request_retry(consumer, APIError(
429, 'code', 'Too Many Requests'), 2)
# we should NOT retry on other client errors
consumer = Consumer(None, 'testsecret')
consumer = Consumer(None, TEST_API_KEY)
api_error = APIError(400, 'code', 'Client Errors')
try:
self._test_request_retry(consumer, api_error, 1)
@@ -159,19 +160,19 @@ class TestConsumer(unittest.TestCase):
self.fail('request() should not retry on client errors')
# test for number of exceptions raise > retries value
consumer = Consumer(None, 'testsecret', retries=3)
consumer = Consumer(None, TEST_API_KEY, retries=3)
self._test_request_retry(consumer, APIError(
500, 'code', 'Internal Server Error'), 3)
def test_pause(self):
consumer = Consumer(None, 'testsecret')
consumer = Consumer(None, TEST_API_KEY)
consumer.pause()
self.assertFalse(consumer.running)
def test_max_batch_size(self):
q = Queue()
consumer = Consumer(
q, 'testsecret', flush_at=100000, flush_interval=3)
q, TEST_API_KEY, flush_at=100000, flush_interval=3)
track = {
'type': 'track',
'event': 'python event',
@@ -189,7 +190,7 @@ class TestConsumer(unittest.TestCase):
% len(data.encode()))
return res
with mock.patch('analytics.request._session.post',
with mock.patch('posthog.request._session.post',
side_effect=mock_post_fn) as mock_post:
consumer.start()
for _ in range(0, n_msgs + 2):
+16 -24
View File
@@ -1,6 +1,6 @@
import unittest
import analytics
import posthog
class TestModule(unittest.TestCase):
@@ -10,40 +10,32 @@ class TestModule(unittest.TestCase):
def setUp(self):
self.failed = False
analytics.api_key = 'testsecret'
analytics.on_error = self.failed
posthog.api_key = 'testsecret'
posthog.on_error = self.failed
def test_no_api_key(self):
analytics.api_key = None
self.assertRaises(Exception, analytics.track)
posthog.api_key = None
self.assertRaises(Exception, posthog.capture)
def test_no_host(self):
analytics.host = None
self.assertRaises(Exception, analytics.track)
posthog.host = None
self.assertRaises(Exception, posthog.capture)
def test_track(self):
analytics.track('distinct_id', 'python module event')
analytics.flush()
posthog.capture('distinct_id', 'python module event')
posthog.flush()
def test_identify(self):
analytics.identify('distinct_id', {'email': 'user@email.com'})
analytics.flush()
def test_group(self):
analytics.group('distinct_id', 'groupId')
analytics.flush()
posthog.identify('distinct_id', {'email': 'user@email.com'})
posthog.flush()
def test_alias(self):
analytics.alias('previousId', 'distinct_id')
analytics.flush()
posthog.alias('previousId', 'distinct_id')
posthog.flush()
def test_page(self):
analytics.page('distinct_id')
analytics.flush()
def test_screen(self):
analytics.screen('distinct_id')
analytics.flush()
posthog.page('distinct_id', 'https://posthog.com/contact')
posthog.flush()
def test_flush(self):
analytics.flush()
posthog.flush()
+4 -3
View File
@@ -4,12 +4,13 @@ import json
import requests
from posthog.request import post, DatetimeSerializer
from posthog.test.utils import TEST_API_KEY
class TestRequests(unittest.TestCase):
def test_valid_request(self):
res = post(batch=[{
res = post(TEST_API_KEY, batch=[{
'distinct_id': 'distinct_id',
'event': 'python event',
'type': 'track'
@@ -37,7 +38,7 @@ class TestRequests(unittest.TestCase):
self.assertEqual(result, expected)
def test_should_not_timeout(self):
res = post(batch=[{
res = post(TEST_API_KEY, batch=[{
'distinct_id': 'distinct_id',
'event': 'python event',
'type': 'track'
@@ -46,7 +47,7 @@ class TestRequests(unittest.TestCase):
def test_should_timeout(self):
with self.assertRaises(requests.ReadTimeout):
post(batch=[{
post('key', batch=[{
'distinct_id': 'distinct_id',
'event': 'python event',
'type': 'track'
+1
View File
@@ -7,6 +7,7 @@ import six
from posthog import utils
TEST_API_KEY = 'kOOlRy2QlMY9jHZQv0bKz0FZyazBUoY8Arj0lFVNjs4'
class TestUtils(unittest.TestCase):
+1 -1
View File
@@ -1 +1 @@
VERSION = '1.0.8'
VERSION = '1.0.11'
+12 -24
View File
@@ -1,4 +1,4 @@
import analytics
import posthog
import argparse
import json
import logging
@@ -12,7 +12,7 @@ def json_hash(str):
if str:
return json.loads(str)
# analytics -method=<method> -posthog-write-key=<posthogWriteKey> [options]
# posthog -method=<method> -posthog-write-key=<posthogWriteKey> [options]
parser = argparse.ArgumentParser(description='send a posthog message')
@@ -45,38 +45,28 @@ def failed(status, msg):
raise Exception(msg)
def track():
analytics.track(options.distinct_id, options.event, anonymous_id=options.anonymousId,
def capture():
posthog.capture(options.distinct_id, options.event, anonymous_id=options.anonymousId,
properties=json_hash(options.properties), context=json_hash(options.context))
def page():
analytics.page(options.distinct_id, name=options.name, anonymous_id=options.anonymousId,
posthog.page(options.distinct_id, name=options.name, anonymous_id=options.anonymousId,
properties=json_hash(options.properties), context=json_hash(options.context))
def screen():
analytics.screen(options.distinct_id, name=options.name, anonymous_id=options.anonymousId,
properties=json_hash(options.properties), context=json_hash(options.context))
def identify():
analytics.identify(options.distinct_id, anonymous_id=options.anonymousId,
posthog.identify(options.distinct_id, anonymous_id=options.anonymousId,
traits=json_hash(options.traits), context=json_hash(options.context))
def group():
analytics.group(options.distinct_id, options.groupId, json_hash(options.traits),
json_hash(options.context), anonymous_id=options.anonymousId)
def unknown():
print()
analytics.api_key = options.writeKey
analytics.on_error = failed
analytics.debug = True
posthog.api_key = options.writeKey
posthog.on_error = failed
posthog.debug = True
log = logging.getLogger('posthog')
ch = logging.StreamHandler()
@@ -84,16 +74,14 @@ ch.setLevel(logging.DEBUG)
log.addHandler(ch)
switcher = {
"track": track,
"capture": capture,
"page": page,
"screen": screen,
"identify": identify,
"group": group
"identify": identify
}
func = switcher.get(options.type)
if func:
func()
analytics.shutdown()
posthog.shutdown()
else:
print("Invalid Message Type " + options.type)