mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 13:41:15 -07:00
Clean up tracker
This commit is contained in:
parent
90647628c9
commit
655a359ef4
1 changed files with 69 additions and 95 deletions
|
@ -20,25 +20,25 @@ import hashlib
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
def generate_uuid(basedata=None):
|
||||||
def generate_uuid(basedata = None):
|
|
||||||
""" Provides a _random_ UUID with no input, or a UUID4-format MD5 checksum of any input data provided """
|
""" Provides a _random_ UUID with no input, or a UUID4-format MD5 checksum of any input data provided """
|
||||||
if basedata is None:
|
if basedata is None:
|
||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
elif isinstance(basedata, basestring):
|
elif isinstance(basedata, basestring):
|
||||||
checksum = hashlib.md5(basedata).hexdigest()
|
checksum = hashlib.md5(basedata).hexdigest()
|
||||||
return '%8s-%4s-%4s-%4s-%12s' % (checksum[0:8], checksum[8:12], checksum[12:16], checksum[16:20], checksum[20:32])
|
return '%8s-%4s-%4s-%4s-%12s' % (
|
||||||
|
checksum[0:8], checksum[8:12], checksum[12:16], checksum[16:20], checksum[20:32])
|
||||||
|
|
||||||
|
|
||||||
class Time(datetime.datetime):
|
class Time(datetime.datetime):
|
||||||
""" Wrappers and convenience methods for processing various time representations """
|
""" Wrappers and convenience methods for processing various time representations """
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_unix(cls, seconds, milliseconds = 0):
|
def from_unix(cls, seconds, milliseconds=0):
|
||||||
""" Produce a full |datetime.datetime| object from a Unix timestamp """
|
""" Produce a full |datetime.datetime| object from a Unix timestamp """
|
||||||
base = list(time.gmtime(seconds))[0:6]
|
base = list(time.gmtime(seconds))[0:6]
|
||||||
base.append(milliseconds * 1000) # microseconds
|
base.append(milliseconds * 1000) # microseconds
|
||||||
return cls(* base)
|
return cls(*base)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_unix(cls, timestamp):
|
def to_unix(cls, timestamp):
|
||||||
|
@ -49,19 +49,18 @@ class Time(datetime.datetime):
|
||||||
return base
|
return base
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def milliseconds_offset(cls, timestamp, now = None):
|
def milliseconds_offset(cls, timestamp, now=None):
|
||||||
""" Offset time (in milliseconds) from a |datetime.datetime| object to now """
|
""" Offset time (in milliseconds) from a |datetime.datetime| object to now """
|
||||||
if isinstance(timestamp, (int, float)):
|
if isinstance(timestamp, (int, float)):
|
||||||
base = timestamp
|
base = timestamp
|
||||||
else:
|
else:
|
||||||
base = cls.to_unix(timestamp)
|
base = cls.to_unix(timestamp)
|
||||||
base = base + (timestamp.microsecond / 1000000)
|
base = base + (timestamp.microsecond / 1000000)
|
||||||
if now is None:
|
if now is None:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
return (now - base) * 1000
|
return (now - base) * 1000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPRequest(object):
|
class HTTPRequest(object):
|
||||||
""" URL Construction and request handling abstraction.
|
""" URL Construction and request handling abstraction.
|
||||||
This is not intended to be used outside this module.
|
This is not intended to be used outside this module.
|
||||||
|
@ -72,37 +71,33 @@ class HTTPRequest(object):
|
||||||
|
|
||||||
endpoint = 'https://www.google-analytics.com/collect'
|
endpoint = 'https://www.google-analytics.com/collect'
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def debug():
|
def debug():
|
||||||
""" Activate debugging on urllib2 """
|
""" Activate debugging on urllib2 """
|
||||||
handler = HTTPSHandler(debuglevel = 1)
|
handler = HTTPSHandler(debuglevel=1)
|
||||||
opener = build_opener(handler)
|
opener = build_opener(handler)
|
||||||
install_opener(opener)
|
install_opener(opener)
|
||||||
|
|
||||||
# Store properties for all requests
|
# Store properties for all requests
|
||||||
def __init__(self, user_agent = None, *args, **opts):
|
def __init__(self, user_agent=None, *args, **opts):
|
||||||
self.user_agent = user_agent or 'Analytics Pros - Universal Analytics (Python)'
|
self.user_agent = user_agent or 'Analytics Pros - Universal Analytics (Python)'
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fixUTF8(cls, data): # Ensure proper encoding for UA's servers...
|
def fixUTF8(cls, data): # Ensure proper encoding for UA's servers...
|
||||||
""" Convert all strings to UTF-8 """
|
""" Convert all strings to UTF-8 """
|
||||||
for key in data:
|
for key in data:
|
||||||
if isinstance(data[ key ], basestring):
|
if isinstance(data[key], basestring):
|
||||||
data[ key ] = data[ key ].encode('utf-8')
|
data[key] = data[key].encode('utf-8')
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
# Apply stored properties to the given dataset & POST to the configured endpoint
|
||||||
|
def send(self, data):
|
||||||
# Apply stored properties to the given dataset & POST to the configured endpoint
|
|
||||||
def send(self, data):
|
|
||||||
request = Request(
|
request = Request(
|
||||||
self.endpoint + '?' + urlencode(self.fixUTF8(data)),
|
self.endpoint + '?' + urlencode(self.fixUTF8(data)),
|
||||||
headers = {
|
headers={
|
||||||
'User-Agent': self.user_agent
|
'User-Agent': self.user_agent
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.open(request)
|
self.open(request)
|
||||||
|
|
||||||
def open(self, request):
|
def open(self, request):
|
||||||
|
@ -120,42 +115,35 @@ class HTTPRequest(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPPost(HTTPRequest):
|
class HTTPPost(HTTPRequest):
|
||||||
|
|
||||||
# Apply stored properties to the given dataset & POST to the configured endpoint
|
# Apply stored properties to the given dataset & POST to the configured endpoint
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
request = Request(
|
request = Request(
|
||||||
self.endpoint,
|
self.endpoint,
|
||||||
data = urlencode(self.fixUTF8(data)),
|
data=urlencode(self.fixUTF8(data)),
|
||||||
headers = {
|
headers={
|
||||||
'User-Agent': self.user_agent
|
'User-Agent': self.user_agent
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.open(request)
|
self.open(request)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Tracker(object):
|
class Tracker(object):
|
||||||
""" Primary tracking interface for Universal Analytics """
|
""" Primary tracking interface for Universal Analytics """
|
||||||
params = None
|
params = None
|
||||||
parameter_alias = {}
|
parameter_alias = {}
|
||||||
valid_hittypes = ('pageview', 'event', 'social', 'screenview', 'transaction', 'item', 'exception', 'timing')
|
valid_hittypes = ('pageview', 'event', 'social', 'screenview', 'transaction', 'item', 'exception', 'timing')
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def alias(cls, typemap, base, *names):
|
def alias(cls, typemap, base, *names):
|
||||||
""" Declare an alternate (humane) name for a measurement protocol parameter """
|
""" Declare an alternate (humane) name for a measurement protocol parameter """
|
||||||
cls.parameter_alias[ base ] = (typemap, base)
|
cls.parameter_alias[base] = (typemap, base)
|
||||||
for i in names:
|
for i in names:
|
||||||
cls.parameter_alias[ i ] = (typemap, base)
|
cls.parameter_alias[i] = (typemap, base)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def coerceParameter(cls, name, value = None):
|
def coerceParameter(cls, name, value=None):
|
||||||
if isinstance(name, basestring) and name[0] == '&':
|
if isinstance(name, basestring) and name[0] == '&':
|
||||||
return name[1:], str(value)
|
return name[1:], str(value)
|
||||||
elif name in cls.parameter_alias:
|
elif name in cls.parameter_alias:
|
||||||
|
@ -164,7 +152,6 @@ class Tracker(object):
|
||||||
else:
|
else:
|
||||||
raise KeyError, 'Parameter "{0}" is not recognized'.format(name)
|
raise KeyError, 'Parameter "{0}" is not recognized'.format(name)
|
||||||
|
|
||||||
|
|
||||||
def payload(self, data):
|
def payload(self, data):
|
||||||
for key, value in data.iteritems():
|
for key, value in data.iteritems():
|
||||||
try:
|
try:
|
||||||
|
@ -172,73 +159,64 @@ class Tracker(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
option_sequence = {
|
option_sequence = {
|
||||||
'pageview': [ (basestring, 'dp') ],
|
'pageview': [(basestring, 'dp')],
|
||||||
'event': [ (basestring, 'ec'), (basestring, 'ea'), (basestring, 'el'), (int, 'ev') ],
|
'event': [(basestring, 'ec'), (basestring, 'ea'), (basestring, 'el'), (int, 'ev')],
|
||||||
'social': [ (basestring, 'sn'), (basestring, 'sa'), (basestring, 'st') ],
|
'social': [(basestring, 'sn'), (basestring, 'sa'), (basestring, 'st')],
|
||||||
'timing': [ (basestring, 'utc'), (basestring, 'utv'), (basestring, 'utt'), (basestring, 'utl') ]
|
'timing': [(basestring, 'utc'), (basestring, 'utv'), (basestring, 'utt'), (basestring, 'utl')]
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def consume_options(cls, data, hittype, args):
|
def consume_options(cls, data, hittype, args):
|
||||||
""" Interpret sequential arguments related to known hittypes based on declared structures """
|
""" Interpret sequential arguments related to known hittypes based on declared structures """
|
||||||
opt_position = 0
|
opt_position = 0
|
||||||
data[ 't' ] = hittype # integrate hit type parameter
|
data['t'] = hittype # integrate hit type parameter
|
||||||
if hittype in cls.option_sequence:
|
if hittype in cls.option_sequence:
|
||||||
for expected_type, optname in cls.option_sequence[ hittype ]:
|
for expected_type, optname in cls.option_sequence[hittype]:
|
||||||
if opt_position < len(args) and isinstance(args[opt_position], expected_type):
|
if opt_position < len(args) and isinstance(args[opt_position], expected_type):
|
||||||
data[ optname ] = args[ opt_position ]
|
data[optname] = args[opt_position]
|
||||||
opt_position += 1
|
opt_position += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def hittime(cls, timestamp = None, age = None, milliseconds = None):
|
def hittime(cls, timestamp=None, age=None, milliseconds=None):
|
||||||
""" Returns an integer represeting the milliseconds offset for a given hit (relative to now) """
|
""" Returns an integer represeting the milliseconds offset for a given hit (relative to now) """
|
||||||
if isinstance(timestamp, (int, float)):
|
if isinstance(timestamp, (int, float)):
|
||||||
return int(Time.milliseconds_offset(Time.from_unix(timestamp, milliseconds = milliseconds)))
|
return int(Time.milliseconds_offset(Time.from_unix(timestamp, milliseconds=milliseconds)))
|
||||||
if isinstance(timestamp, datetime.datetime):
|
if isinstance(timestamp, datetime.datetime):
|
||||||
return int(Time.milliseconds_offset(timestamp))
|
return int(Time.milliseconds_offset(timestamp))
|
||||||
if isinstance(age, (int, float)):
|
if isinstance(age, (int, float)):
|
||||||
return int(age * 1000) + (milliseconds or 0)
|
return int(age * 1000) + (milliseconds or 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def account(self):
|
def account(self):
|
||||||
return self.params.get('tid', None)
|
return self.params.get('tid', None)
|
||||||
|
|
||||||
|
def __init__(self, account, name=None, client_id=None, hash_client_id=False, user_id=None, user_agent=None,
|
||||||
|
use_post=True):
|
||||||
|
|
||||||
def __init__(self, account, name = None, client_id = None, hash_client_id = False, user_id = None, user_agent = None, use_post = True):
|
|
||||||
|
|
||||||
if use_post is False:
|
if use_post is False:
|
||||||
self.http = HTTPRequest(user_agent = user_agent)
|
self.http = HTTPRequest(user_agent=user_agent)
|
||||||
else:
|
else:
|
||||||
self.http = HTTPPost(user_agent = user_agent)
|
self.http = HTTPPost(user_agent=user_agent)
|
||||||
|
|
||||||
self.params = { 'v': 1, 'tid': account }
|
self.params = {'v': 1, 'tid': account}
|
||||||
|
|
||||||
if client_id is None:
|
if client_id is None:
|
||||||
client_id = generate_uuid()
|
client_id = generate_uuid()
|
||||||
|
|
||||||
self.params[ 'cid' ] = client_id
|
self.params['cid'] = client_id
|
||||||
|
|
||||||
self.hash_client_id = hash_client_id
|
self.hash_client_id = hash_client_id
|
||||||
|
|
||||||
if user_id is not None:
|
if user_id is not None:
|
||||||
self.params[ 'uid' ] = user_id
|
self.params['uid'] = user_id
|
||||||
|
|
||||||
|
|
||||||
def set_timestamp(self, data):
|
def set_timestamp(self, data):
|
||||||
""" Interpret time-related options, apply queue-time parameter as needed """
|
""" Interpret time-related options, apply queue-time parameter as needed """
|
||||||
if 'hittime' in data: # an absolute timestamp
|
if 'hittime' in data: # an absolute timestamp
|
||||||
data['qt'] = self.hittime(timestamp = data.pop('hittime', None))
|
data['qt'] = self.hittime(timestamp=data.pop('hittime', None))
|
||||||
if 'hitage' in data: # a relative age (in seconds)
|
if 'hitage' in data: # a relative age (in seconds)
|
||||||
data['qt'] = self.hittime(age = data.pop('hitage', None))
|
data['qt'] = self.hittime(age=data.pop('hitage', None))
|
||||||
|
|
||||||
|
|
||||||
def send(self, hittype, *args, **data):
|
def send(self, hittype, *args, **data):
|
||||||
""" Transmit HTTP requests to Google Analytics using the measurement protocol """
|
""" Transmit HTTP requests to Google Analytics using the measurement protocol """
|
||||||
|
@ -249,44 +227,38 @@ class Tracker(object):
|
||||||
self.set_timestamp(data)
|
self.set_timestamp(data)
|
||||||
self.consume_options(data, hittype, args)
|
self.consume_options(data, hittype, args)
|
||||||
|
|
||||||
for item in args: # process dictionary-object arguments of transcient data
|
for item in args: # process dictionary-object arguments of transcient data
|
||||||
if isinstance(item, dict):
|
if isinstance(item, dict):
|
||||||
for key, val in self.payload(item):
|
for key, val in self.payload(item):
|
||||||
data[ key ] = val
|
data[key] = val
|
||||||
|
|
||||||
for k, v in self.params.iteritems(): # update only absent parameters
|
for k, v in self.params.iteritems(): # update only absent parameters
|
||||||
if k not in data:
|
if k not in data:
|
||||||
data[ k ] = v
|
data[k] = v
|
||||||
|
|
||||||
|
|
||||||
data = dict(self.payload(data))
|
data = dict(self.payload(data))
|
||||||
|
|
||||||
if self.hash_client_id:
|
if self.hash_client_id:
|
||||||
data[ 'cid' ] = generate_uuid(data[ 'cid' ])
|
data['cid'] = generate_uuid(data['cid'])
|
||||||
|
|
||||||
# Transmit the hit to Google...
|
# Transmit the hit to Google...
|
||||||
self.http.send(data)
|
self.http.send(data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Setting persistent attibutes of the session/hit/etc (inc. custom dimensions/metrics)
|
# Setting persistent attibutes of the session/hit/etc (inc. custom dimensions/metrics)
|
||||||
def set(self, name, value = None):
|
def set(self, name, value=None):
|
||||||
if isinstance(name, dict):
|
if isinstance(name, dict):
|
||||||
for key, value in name.iteritems():
|
for key, value in name.iteritems():
|
||||||
try:
|
try:
|
||||||
param, value = self.coerceParameter(key, value)
|
param, value = self.coerceParameter(key, value)
|
||||||
self.params[param] = value
|
self.params[param] = value
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
elif isinstance(name, basestring):
|
elif isinstance(name, basestring):
|
||||||
try:
|
try:
|
||||||
param, value = self.coerceParameter(name, value)
|
param, value = self.coerceParameter(name, value)
|
||||||
self.params[param] = value
|
self.params[param] = value
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __getitem__(self, name):
|
def __getitem__(self, name):
|
||||||
param, value = self.coerceParameter(name, None)
|
param, value = self.coerceParameter(name, None)
|
||||||
|
@ -301,6 +273,7 @@ class Tracker(object):
|
||||||
if param in self.params:
|
if param in self.params:
|
||||||
del self.params[param]
|
del self.params[param]
|
||||||
|
|
||||||
|
|
||||||
def safe_unicode(obj):
|
def safe_unicode(obj):
|
||||||
""" Safe convertion to the Unicode string version of the object """
|
""" Safe convertion to the Unicode string version of the object """
|
||||||
try:
|
try:
|
||||||
|
@ -313,7 +286,7 @@ def safe_unicode(obj):
|
||||||
MAX_CUSTOM_DEFINITIONS = 200
|
MAX_CUSTOM_DEFINITIONS = 200
|
||||||
MAX_EC_LISTS = 11 # 1-based index
|
MAX_EC_LISTS = 11 # 1-based index
|
||||||
MAX_EC_PRODUCTS = 11 # 1-based index
|
MAX_EC_PRODUCTS = 11 # 1-based index
|
||||||
MAX_EC_PROMOTIONS = 11 # 1-based index
|
MAX_EC_PROMOTIONS = 11 # 1-based index
|
||||||
|
|
||||||
Tracker.alias(int, 'v', 'protocol-version')
|
Tracker.alias(int, 'v', 'protocol-version')
|
||||||
Tracker.alias(safe_unicode, 'cid', 'client-id', 'clientId', 'clientid')
|
Tracker.alias(safe_unicode, 'cid', 'client-id', 'clientId', 'clientid')
|
||||||
|
@ -331,7 +304,6 @@ Tracker.alias(int, 'qt', 'queueTime', 'queue-time')
|
||||||
Tracker.alias(safe_unicode, 't', 'hitType', 'hittype')
|
Tracker.alias(safe_unicode, 't', 'hitType', 'hittype')
|
||||||
Tracker.alias(int, 'aip', 'anonymizeIp', 'anonIp', 'anonymize-ip')
|
Tracker.alias(int, 'aip', 'anonymizeIp', 'anonIp', 'anonymize-ip')
|
||||||
|
|
||||||
|
|
||||||
# Campaign attribution
|
# Campaign attribution
|
||||||
Tracker.alias(safe_unicode, 'cn', 'campaign', 'campaignName', 'campaign-name')
|
Tracker.alias(safe_unicode, 'cn', 'campaign', 'campaignName', 'campaign-name')
|
||||||
Tracker.alias(safe_unicode, 'cs', 'source', 'campaignSource', 'campaign-source')
|
Tracker.alias(safe_unicode, 'cs', 'source', 'campaignSource', 'campaign-source')
|
||||||
|
@ -360,7 +332,8 @@ Tracker.alias(safe_unicode, 'ti', 'transaction', 'transactionId', 'transaction-i
|
||||||
Tracker.alias(float, 'tr', 'revenue', 'transactionRevenue', 'transaction-revenue')
|
Tracker.alias(float, 'tr', 'revenue', 'transactionRevenue', 'transaction-revenue')
|
||||||
Tracker.alias(float, 'ts', 'shipping', 'transactionShipping', 'transaction-shipping')
|
Tracker.alias(float, 'ts', 'shipping', 'transactionShipping', 'transaction-shipping')
|
||||||
Tracker.alias(float, 'tt', 'tax', 'transactionTax', 'transaction-tax')
|
Tracker.alias(float, 'tt', 'tax', 'transactionTax', 'transaction-tax')
|
||||||
Tracker.alias(safe_unicode, 'cu', 'currency', 'transactionCurrency', 'transaction-currency') # Currency code, e.g. USD, EUR
|
Tracker.alias(safe_unicode, 'cu', 'currency', 'transactionCurrency',
|
||||||
|
'transaction-currency') # Currency code, e.g. USD, EUR
|
||||||
Tracker.alias(safe_unicode, 'in', 'item-name', 'itemName')
|
Tracker.alias(safe_unicode, 'in', 'item-name', 'itemName')
|
||||||
Tracker.alias(float, 'ip', 'item-price', 'itemPrice')
|
Tracker.alias(float, 'ip', 'item-price', 'itemPrice')
|
||||||
Tracker.alias(float, 'iq', 'item-quantity', 'itemQuantity')
|
Tracker.alias(float, 'iq', 'item-quantity', 'itemQuantity')
|
||||||
|
@ -374,7 +347,6 @@ Tracker.alias(safe_unicode, 'el', 'event-label', 'eventLabel', 'label')
|
||||||
Tracker.alias(int, 'ev', 'event-value', 'eventValue', 'value')
|
Tracker.alias(int, 'ev', 'event-value', 'eventValue', 'value')
|
||||||
Tracker.alias(int, 'ni', 'noninteractive', 'nonInteractive', 'noninteraction', 'nonInteraction')
|
Tracker.alias(int, 'ni', 'noninteractive', 'nonInteractive', 'noninteraction', 'nonInteraction')
|
||||||
|
|
||||||
|
|
||||||
# Social
|
# Social
|
||||||
Tracker.alias(safe_unicode, 'sa', 'social-action', 'socialAction')
|
Tracker.alias(safe_unicode, 'sa', 'social-action', 'socialAction')
|
||||||
Tracker.alias(safe_unicode, 'sn', 'social-network', 'socialNetwork')
|
Tracker.alias(safe_unicode, 'sn', 'social-network', 'socialNetwork')
|
||||||
|
@ -396,7 +368,7 @@ Tracker.alias(safe_unicode, 'tcp', 'timingTCPConnect', 'timing-tcp-connect')
|
||||||
Tracker.alias(safe_unicode, 'srt', 'timingServerResponse', 'timing-server-response')
|
Tracker.alias(safe_unicode, 'srt', 'timingServerResponse', 'timing-server-response')
|
||||||
|
|
||||||
# Custom dimensions and metrics
|
# Custom dimensions and metrics
|
||||||
for i in range(0,200):
|
for i in range(0, 200):
|
||||||
Tracker.alias(safe_unicode, 'cd{0}'.format(i), 'dimension{0}'.format(i))
|
Tracker.alias(safe_unicode, 'cd{0}'.format(i), 'dimension{0}'.format(i))
|
||||||
Tracker.alias(int, 'cm{0}'.format(i), 'metric{0}'.format(i))
|
Tracker.alias(int, 'cm{0}'.format(i), 'metric{0}'.format(i))
|
||||||
|
|
||||||
|
@ -419,7 +391,7 @@ for product_index in range(1, MAX_EC_PRODUCTS):
|
||||||
Tracker.alias(int, 'pr{0}qt'.format(product_index)) # Product quantity
|
Tracker.alias(int, 'pr{0}qt'.format(product_index)) # Product quantity
|
||||||
Tracker.alias(str, 'pr{0}cc'.format(product_index)) # Product coupon code
|
Tracker.alias(str, 'pr{0}cc'.format(product_index)) # Product coupon code
|
||||||
Tracker.alias(int, 'pr{0}ps'.format(product_index)) # Product position
|
Tracker.alias(int, 'pr{0}ps'.format(product_index)) # Product position
|
||||||
|
|
||||||
for custom_index in range(MAX_CUSTOM_DEFINITIONS):
|
for custom_index in range(MAX_CUSTOM_DEFINITIONS):
|
||||||
Tracker.alias(str, 'pr{0}cd{1}'.format(product_index, custom_index)) # Product custom dimension
|
Tracker.alias(str, 'pr{0}cd{1}'.format(product_index, custom_index)) # Product custom dimension
|
||||||
Tracker.alias(int, 'pr{0}cm{1}'.format(product_index, custom_index)) # Product custom metric
|
Tracker.alias(int, 'pr{0}cm{1}'.format(product_index, custom_index)) # Product custom metric
|
||||||
|
@ -434,8 +406,10 @@ for product_index in range(1, MAX_EC_PRODUCTS):
|
||||||
Tracker.alias(int, 'il{0}pi{1}pr'.format(list_index, product_index)) # Product impression price
|
Tracker.alias(int, 'il{0}pi{1}pr'.format(list_index, product_index)) # Product impression price
|
||||||
|
|
||||||
for custom_index in range(MAX_CUSTOM_DEFINITIONS):
|
for custom_index in range(MAX_CUSTOM_DEFINITIONS):
|
||||||
Tracker.alias(str, 'il{0}pi{1}cd{2}'.format(list_index, product_index, custom_index)) # Product impression custom dimension
|
Tracker.alias(str, 'il{0}pi{1}cd{2}'.format(list_index, product_index,
|
||||||
Tracker.alias(int, 'il{0}pi{1}cm{2}'.format(list_index, product_index, custom_index)) # Product impression custom metric
|
custom_index)) # Product impression custom dimension
|
||||||
|
Tracker.alias(int, 'il{0}pi{1}cm{2}'.format(list_index, product_index,
|
||||||
|
custom_index)) # Product impression custom metric
|
||||||
|
|
||||||
for list_index in range(1, MAX_EC_LISTS):
|
for list_index in range(1, MAX_EC_LISTS):
|
||||||
Tracker.alias(unicode, 'il{0}nm'.format(list_index)) # Product impression list name
|
Tracker.alias(unicode, 'il{0}nm'.format(list_index)) # Product impression list name
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue