Skip to content

Commit

Permalink
Merge c5ab10c into a72f33a
Browse files Browse the repository at this point in the history
  • Loading branch information
lenards committed Nov 13, 2013
2 parents a72f33a + c5ab10c commit 0f89980
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 2 deletions.
14 changes: 14 additions & 0 deletions KISSmetrics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
# -*- coding: utf-8 -*-

#: Default host for tracking service endpoint
TRACKING_HOSTNAME = 'trk.kissmetrics.com'

#: Default scheme for requests to tracking service endpoint
TRACKING_SCHEME = 'http'

#: Path to record events via tracking service
#:
#: .. seealso:: http://support.kissmetrics.com/apis/specifications#recording-an-event
RECORD_PATH = 'e'

#: Path to set a property via tracking service
#:
#: .. seealso:: http://support.kissmetrics.com/apis/specifications#setting-properties
SET_PATH = 's'

#: Path to alias two identities via tracking service
#:
#: .. seealso:: http://support.kissmetrics.com/apis/specifications#aliasing-users
ALIAS_PATH = 'a'

__author__ = 'Ernest W. Durbin III <ewdurbin@gmail.com>'
Expand Down
73 changes: 73 additions & 0 deletions KISSmetrics/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@


class Client:
"""Interface to KISSmetrics tracking service"""

def __init__(self, key, trk_host=KISSmetrics.TRACKING_HOSTNAME,
trk_scheme=KISSmetrics.TRACKING_SCHEME):
"""Initialize client for use with KISSmetrics API key.
:param key: API key for product, found on the
"KISSmetrics Settings".
:type key: str
:param trk_host: tracking host for requests; defaults
production tracking service.
:param trk_proto: the protocol for requests; either be `'http'`
or `'https'`.
"""
self.key = key
if trk_scheme not in ['http', 'https']:
raise ValueError('trk_scheme must be one of (http, https)')
Expand All @@ -21,6 +34,21 @@ def request(self, uri, method="GET"):

def record(self, person, event, properties=None, timestamp=None,
path=KISSmetrics.RECORD_PATH):
"""Record `event` for `person` with any `properties`.
:param person: the individual performing the `event`
:param event: the `event` name that was performed
:param properties: any additional data to include
:type properties: dict
:param timestamp: when the `event` was performed; optional for
back-dating
:param path: HTTP endpoint to use; defaults to
``KISSmetrics.RECORD_PATH``
:returns: an HTTP response for the request
:rtype: `urllib3.response.HTTPResponse`
"""
this_request = request.record(self.key, person, event,
timestamp=timestamp,
properties=properties,
Expand All @@ -30,13 +58,58 @@ def record(self, person, event, properties=None, timestamp=None,

def set(self, person, properties=None, timestamp=None,
path=KISSmetrics.SET_PATH):
"""Set a property (or properties) for a `person`.
:param person: individual to associate properties with
:param properties: key-value pairs to associate with `person`
:type properties: dict
:param timestamp: when the `event` was performed; optional for
back-dating
:param path: HTTP endpoint to use; defaults to
``KISSmetrics.SET_PATH``
:returns: an HTTP response for the request
:rtype: `urllib3.response.HTTPResponse`
"""
this_request = request.set(self.key, person, timestamp=timestamp,
properties=properties,
scheme=self.trk_scheme, host=self.trk_host,
path=path)
return self.request(this_request)

def alias(self, person, identity, path=KISSmetrics.ALIAS_PATH):
"""Map `person` to `identity`; actions done by one resolve to other.
:param person: consider as same individual ``identity``; the
source of the alias operation
:type person: str or unicode
:param identity: consider as an alias of ``person``; the target
of the alias operation
:type identity: str or unicode
:param path: HTTP endpoint to use; defaults to
``KISSmetrics.ALIAS_PATH``
:returns: an HTTP response for the request
:rtype: `urllib3.response.HTTPResponse`
Note the direction of the mapping is ``person`` to ``identity``
(so "``person`` is also known as ``identity``" or "``person`` =>
``identity``" when looking at it as "``<source>`` => ``<target>``")
When consulting the Aliasing documentation, `person` corresponds
to ``query_string.PERSON_PARAM`` and `identity` corresponds to
``query_string.ALIAS_PARAM``.
Aliasing is not a reversible operation. When aliasing to an
identity, take care not to use a session identifier or any other
value that is not relatively stable (a value that will not
change per request or per session).
For more information see the API Specifications on `Aliasing
<http://support.kissmetrics.com/apis/specifications.html#aliasing-users>`_.
"""
this_request = request.alias(self.key, person, identity,
scheme=self.trk_scheme,
host=self.trk_host, path=path)
Expand Down
84 changes: 82 additions & 2 deletions KISSmetrics/client_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,26 @@


class ClientCompat:
"""Compatibility interface to KISSmetrics tracking service
.. warning::
Interface only exists for compatibility and will not be supported
in the future.
"""

def __init__(self, key, host=None, http_timeout=None, logging=True):
"""Initialize client for use with KISSmetrics API key.
:param key: API key for a product, found on the
"KISSmetrics Settings".
:type key: str
:param host: tracking host for requests; defaults
production tracking service.
:param http_timeout: request timeout; defaults to None
:type http_timeout: int
:param logging: indicate whether to log
"""
self.key = key
if host:
(trk_host, trk_port) = host.split(':')
Expand All @@ -27,10 +45,30 @@ def __init__(self, key, host=None, http_timeout=None, logging=True):
self.identity = None

def identify(self, identity):
"""Define identity for subsequent calls
:param identity: identifying info (email, user-id, anonymous-id)
:type identity: str or unicode
"""
self.identity = identity

def record(self, action, props=None, path=KISSmetrics.RECORD_PATH,
resp=False):
"""Record event for identity with any properties.
:param action: event performed
:param props: any additional data to include
:type props: dict
:param resp: indicate whether to return response
:type resp: boolean
:returns: an HTTP response for request if `resp=True`
:rtype: `urllib3.response.HTTPResponse`
:raises: Exception if either `identity` or `key` not set
"""
self.check_id_key()
timestamp = None
if not props:
Expand All @@ -42,6 +80,20 @@ def record(self, action, props=None, path=KISSmetrics.RECORD_PATH,
return response

def set(self, data, path=KISSmetrics.SET_PATH, resp=False):
"""Set a properties provided in `data` for identity.
:param data: key-value pairs to associate with identity
:type data: dict
:param path: endpoint path; defaults to ``KISSmetrics.SET_PATH``
:param resp: indicate whether to return response
:type resp: boolean
:returns: an HTTP response for request if `resp=True`
:rtype: `urllib3.response.HTTPResponse`
:raises: Exception if either `identity` or `key` not set
"""
self.check_id_key()
timestamp = None
response = self.client.set(person=self.identity, properties=data,
Expand All @@ -50,25 +102,53 @@ def set(self, data, path=KISSmetrics.SET_PATH, resp=False):
return response

def alias(self, name, alias_to, path=KISSmetrics.ALIAS_PATH, resp=False):
"""Map `name` to `alias_to`; actions done by one resolve to other.
:param name: consider as same individual as ``alias_to``
:param alias_to: consider an alias of ``name``
:param path: endpoint path; defaults to ``KISSmetrics.ALIAS_PATH``
:param resp: indicate whether to return response
:type resp: boolean
:returns: an HTTP response for request if `resp=True`
:rtype: `urllib3.response.HTTPResponse`
:raises: Exception if either `identity` or `key` not set
"""
self.check_init()
response = self.client.alias(person=name, identity=alias_to, path=path)
if resp:
return response

def log_file(self):
"""Retrieve path to log file.
.. note::
Will log to ``'/tmp/kissmetrics_error.log'``; cannot be modified.
"""
return '/tmp/kissmetrics_error.log'

def reset(self):
"""Reset `identity` and `key` attributes
.. warning::
After calling this method, further calls to `record`, `set`, &
`alias` will raise an `Exception`. You will need to set the API
key again via `key` attribute and call `identify`.
"""
self.identity = None
self.key = None

def check_identify(self):
if self.identity is None:
raise Exception('Need to identify first (KM.identify <user>)')
raise Exception('Need to identify first: KM.identify(<user>)')

def check_init(self):
if self.key is None:
raise Exception('Need to initialize first (KM.init <your_key>)')
raise Exception('Need to initialize first: KM(<your_key>)')

def now(self):
return datetime.datetime.utcnow()
Expand Down
20 changes: 20 additions & 0 deletions KISSmetrics/query_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@

def create_query(key, person, event=None, timestamp=None,
identity=None, properties=None):
"""Build and encode query string
:param key: API key for product, found on the
"KISSmetrics Settings".
:param person: individual performing `event`
:param event: event name that was performed
:param timestamp: when `event` was performed; optional for
back-dating
:param identity: individual to alias to `person`
:param properties: any additional data to include
:type properties: dict
:returns: URL encoded string representing query string
:rtype: str
.. note::
When a ``timestamp`` is provided, the ``TIME_FLAG_KEY`` will
be set to ``1`` and included.
"""
if properties is None:
properties = {}

Expand Down

0 comments on commit 0f89980

Please sign in to comment.