Compare commits

..
23 Commits
Author SHA1 Message Date
Neil Kakkar 372fb74637 Release 1.3.1 2021-05-07 12:22:14 +01:00
Neil KakkarandGitHub ba11548089 revert test change (#28) 2021-05-07 12:39:50 +02:00
Neil KakkarandGitHub 49d0821e27 Merge pull request #23 from PostHog/set_once
Add $set and $set_once support
2021-05-07 11:06:04 +01:00
Neil Kakkar b4489f1dca black 2021-05-07 10:57:53 +01:00
Neil Kakkar 8040964761 Add set and set_once to simulator.py 2021-05-07 10:54:10 +01:00
Tim GlaserandGitHub 4f853403b9 Merge pull request #27 from gagantrivedi/fix/alias
fix: Add distinct id to $create_alias event
2021-05-07 11:35:00 +02:00
Gagan ac61fb0e01 chore: Reformat consumer with black 2021-05-07 14:52:25 +05:30
Gagan 7196dc6048 fix: Add distinct id to $create_alias event 2021-05-07 11:22:03 +05:30
Neil Kakkar 45303b899e black 2021-05-06 14:05:57 +01:00
Neil Kakkar 7e463ccad6 resolve conflicts with master 2021-05-06 13:59:46 +01:00
Neil Kakkar 41dec34929 remove API key 2021-05-06 13:53:18 +01:00
Neil Kakkar 0bf9db0108 add functionality+tests for set and set_once 2021-05-06 13:52:23 +01:00
Neil Kakkar e8308360bb cleanup tests, add instructions to readme 2021-05-06 11:43:13 +01:00
Paolo D'AmicoandGitHub 15ebe85a78 Merge pull request #26 from PostHog/distinct-uuid 2021-04-01 17:59:29 -07:00
Michael Matloka dbc22d2f9f Add UUID to ID_TYPES 2021-04-01 17:31:48 +02:00
Paolo D'Amico f3ee238823 black 2021-02-08 11:21:55 +01:00
Paolo D'Amico 71d81b2da9 update tests 2021-02-08 11:18:55 +01:00
Paolo D'Amico 5493029577 add $set_once support 2021-02-08 11:16:27 +01:00
Michael Matloka d66f944571 Bump version to 1.2.1 2021-02-05 16:27:51 +01:00
Michael MatlokaandGitHub 9d620967f8 Merge pull request #22 from PostHog/fix-possibly-null-percentage
Add condition for rollout-percentage
2021-01-28 15:01:30 +01:00
Eric 563404f914 add condition 2021-01-25 19:57:21 -05:00
Yakko MajuriandGitHub d15aac41a9 Update README.md 2021-01-11 17:19:21 -03:00
Michael MatlokaandGitHub 8a3e28b949 Merge pull request #21 from PostHog/code-style
Black and isort all the things
2021-01-05 12:25:57 +01:00
16 changed files with 244 additions and 22 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
- name: Install requirements.txt dependencies with pip
run: |
python -m pip install -e .
python -m pip install -e .[test]
- name: Run posthog tests
run: |
+3 -1
View File
@@ -5,7 +5,7 @@ dist/
MANIFEST
build/
.eggs/
.coverage/
.coverage
.vscode/
env/
venv/
@@ -13,3 +13,5 @@ flake8.out
pylint.out
posthog-analytics
.idea
.python-version
.coverage
+6 -3
View File
@@ -1,7 +1,10 @@
test:
lint:
pylint --rcfile=.pylintrc --reports=y --exit-zero analytics | tee pylint.out
flake8 --max-complexity=10 --statistics analytics > flake8.out || true
coverage run --branch --include=analytics/\* --omit=*/test* setup.py test
test:
coverage run -m pytest
coverage report
release:
rm -rf dist/*
@@ -26,4 +29,4 @@ release_analytics:
e2e_test:
.buildscripts/e2e.sh
.PHONY: test release e2e_test
.PHONY: test lint release e2e_test
+17
View File
@@ -3,3 +3,20 @@
Please see the main [PostHog docs](https://posthog.com/docs).
Specifically, the [Python integration](https://posthog.com/docs/integrations/python-integration) details.
## Questions?
### [Join our Slack community.](https://join.slack.com/t/posthogusers/shared_invite/enQtOTY0MzU5NjAwMDY3LTc2MWQ0OTZlNjhkODk3ZDI3NDVjMDE1YjgxY2I4ZjI4MzJhZmVmNjJkN2NmMGJmMzc2N2U3Yjc3ZjI5NGFlZDQ)
# Local Development
## Testing Locally
1. Run `python3 -m venv env` (creates virtual environment called "env")
2. Run `source env/bin/activate` (activates the virtual environment)
3. Run `python3 -m pip install -e ".[test]"` (installs the package in develop mode, along with test dependencies)
4. Run `make test`
## Running Locally
Assuming you have a [local version of PostHog](https://posthog.com/docs/developing-locally) running, you can run `python3 example.py` to see the library in action.
+19 -2
View File
@@ -19,12 +19,29 @@ posthog.capture("distinct_id", "event", {"property1": "value", "property2": "val
print(posthog.feature_enabled("beta-feature", "distinct_id"))
print("sleeping")
time.sleep(45)
time.sleep(5)
print(posthog.feature_enabled("beta-feature", "distinct_id"))
# # Alias a previous distinct id with a new one
posthog.alias("distinct_id", "new_distinct_id")
posthog.capture("new_distinct_id", "event2", {"property1": "value", "property2": "value"})
# # Add properties to the person
posthog.identify("distinct_id", {"email": "something@something.com"})
posthog.identify("new_distinct_id", {"email": "something@something.com"})
# properties set only once to the person
posthog.set_once("new_distinct_id", {"self_serve_signup": True})
time.sleep(3)
posthog.set_once(
"new_distinct_id", {"self_serve_signup": False}
) # this will not change the property (because it was already set)
posthog.set("new_distinct_id", {"current_browser": "Chrome"})
posthog.set("new_distinct_id", {"current_browser": "Firefox"})
posthog.shutdown()
+67 -1
View File
@@ -32,7 +32,7 @@ def capture(
A `capture` call requires
- `distinct id` which uniquely identifies your user
- `event name` to make sure
- `event name` to specify the event
- We recommend using [verb] [noun], like `movie played` or `movie updated` to easily identify what your events mean later on.
Optionally you can submit
@@ -87,6 +87,72 @@ def identify(
)
def set(
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
"""
Set properties on a user record.
This will overwrite previous people property values, just like `identify`.
A `set` call requires
- `distinct id` which uniquely identifies your user
- `properties` with a dict with any key: value pairs
For example:
```python
posthog.set('distinct id', {
'current_browser': 'Chrome',
})
```
"""
_proxy(
"set",
distinct_id=distinct_id,
properties=properties,
context=context,
timestamp=timestamp,
message_id=message_id,
)
def set_once(
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
"""
Set properties on a user record, only if they do not yet exist.
This will not overwrite previous people property values, unlike `identify`.
A `set_once` call requires
- `distinct id` which uniquely identifies your user
- `properties` with a dict with any key: value pairs
For example:
```python
posthog.set_once('distinct id', {
'referred_by': 'friend',
})
```
"""
_proxy(
"set_once",
distinct_id=distinct_id,
properties=properties,
context=context,
timestamp=timestamp,
message_id=message_id,
)
def group(*args, **kwargs):
"""Send a group call."""
_proxy("group", *args, **kwargs)
+38 -4
View File
@@ -3,9 +3,8 @@ import hashlib
import logging
import numbers
from datetime import datetime, timedelta
from uuid import uuid4
from uuid import UUID, uuid4
import requests
from dateutil.tz import tzutc
from six import string_types
@@ -21,7 +20,7 @@ except ImportError:
import Queue as queue
ID_TYPES = (numbers.Number, string_types)
ID_TYPES = (numbers.Number, string_types, UUID)
__LONG_SCALE__ = float(0xFFFFFFFFFFFFFFF)
@@ -138,6 +137,40 @@ class Client(object):
return self._enqueue(msg)
def set(self, distinct_id=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)
msg = {
"timestamp": timestamp,
"context": context,
"distinct_id": distinct_id,
"$set": properties,
"event": "$set",
"messageId": message_id,
}
return self._enqueue(msg)
def set_once(self, distinct_id=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)
msg = {
"timestamp": timestamp,
"context": context,
"distinct_id": distinct_id,
"$set_once": properties,
"event": "$set_once",
"messageId": message_id,
}
return self._enqueue(msg)
def alias(self, previous_id=None, distinct_id=None, context=None, timestamp=None, message_id=None):
context = context or {}
@@ -152,6 +185,7 @@ class Client(object):
"timestamp": timestamp,
"context": context,
"event": "$create_alias",
"distinct_id": previous_id,
}
return self._enqueue(msg)
@@ -296,7 +330,7 @@ class Client(object):
except IndexError:
return default
if feature_flag.get("is_simple_flag"):
if feature_flag.get("is_simple_flag") and feature_flag.get("rollout_percentage"):
response = _hash(key, distinct_id) <= (feature_flag["rollout_percentage"] / 100)
else:
try:
+1 -1
View File
@@ -117,7 +117,7 @@ class Consumer(Thread):
return items
def request(self, batch):
"""Attempt to upload the batch and retry before raising an error """
"""Attempt to upload the batch and retry before raising an error"""
def fatal_exception(exc):
if isinstance(exc, APIError):
@@ -1,6 +1,7 @@
import time
import unittest
from datetime import date, datetime
from uuid import uuid4
import mock
import six
@@ -8,7 +9,7 @@ from freezegun import freeze_time
from posthog.client import Client
from posthog.request import APIError
from posthog.test.utils import TEST_API_KEY
from posthog.test.test_utils import TEST_API_KEY
from posthog.version import VERSION
@@ -104,6 +105,64 @@ class TestClient(unittest.TestCase):
self.assertEqual(msg["messageId"], "messageId")
self.assertEqual(msg["distinct_id"], "distinct_id")
def test_basic_set(self):
client = self.client
success, msg = client.set("distinct_id", {"trait": "value"})
client.flush()
self.assertTrue(success)
self.assertFalse(self.failed)
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")
def test_advanced_set(self):
client = self.client
success, msg = client.set(
"distinct_id", {"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["$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")
def test_basic_set_once(self):
client = self.client
success, msg = client.set_once("distinct_id", {"trait": "value"})
client.flush()
self.assertTrue(success)
self.assertFalse(self.failed)
self.assertEqual(msg["$set_once"]["trait"], "value")
self.assertTrue(isinstance(msg["timestamp"], str))
self.assertTrue(isinstance(msg["messageId"], str))
self.assertEqual(msg["distinct_id"], "distinct_id")
def test_advanced_set_once(self):
client = self.client
success, msg = client.set_once(
"distinct_id", {"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["$set_once"]["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")
def test_basic_alias(self):
client = self.client
success, msg = client.alias("previousId", "distinct_id")
@@ -122,6 +181,16 @@ class TestClient(unittest.TestCase):
self.assertEqual(msg["distinct_id"], "distinct_id")
self.assertEqual(msg["properties"]["$current_url"], "https://posthog.com/contact")
def test_basic_page_distinct_uuid(self):
client = self.client
distinct_id = uuid4()
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"], str(distinct_id))
self.assertEqual(msg["properties"]["$current_url"], "https://posthog.com/contact")
def test_advanced_page(self):
client = self.client
success, msg = client.page(
@@ -11,7 +11,7 @@ except ImportError:
from posthog.consumer import MAX_MSG_SIZE, Consumer
from posthog.request import APIError
from posthog.test.utils import TEST_API_KEY
from posthog.test.test_utils import TEST_API_KEY
class TestConsumer(unittest.TestCase):
@@ -5,7 +5,7 @@ from datetime import date, datetime
import requests
from posthog.request import DatetimeSerializer, batch_post
from posthog.test.utils import TEST_API_KEY
from posthog.test.test_utils import TEST_API_KEY
class TestRequests(unittest.TestCase):
+1 -1
View File
@@ -1 +1 @@
VERSION = "1.1.3"
VERSION = "1.3.1"
+2 -4
View File
@@ -21,11 +21,10 @@ extras_require = {
"black",
"isort",
"pre-commit",
]
],
"test": ["mock>=2.0.0", "freezegun==0.3.15", "pylint", "flake8", "coverage"],
}
tests_require = ["mock>=2.0.0", "freezegun==0.3.15"]
setup(
name="posthog",
version=VERSION,
@@ -39,7 +38,6 @@ setup(
license="MIT License",
install_requires=install_requires,
extras_require=extras_require,
tests_require=tests_require,
description="Integrate PostHog into any python application.",
long_description=long_description,
classifiers=[
+17 -1
View File
@@ -71,6 +71,22 @@ def identify():
)
def set_once():
posthog.set_once(
options.distinct_id,
properties=json_hash(options.traits),
context=json_hash(options.context),
)
def set():
posthog.set(
options.distinct_id,
properties=json_hash(options.traits),
context=json_hash(options.context),
)
def unknown():
print()
@@ -84,7 +100,7 @@ ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
log.addHandler(ch)
switcher = {"capture": capture, "page": page, "identify": identify}
switcher = {"capture": capture, "page": page, "identify": identify, "set_once": set_once, "set": set}
func = switcher.get(options.type)
if func: