Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tag: v0.9.4
Fetching contributors…

Cannot retrieve contributors at this time

executable file 221 lines (173 sloc) 6.733 kB
#!/usr/bin/env python
"""This module defines the Yeoman Insight metrics reporter tool."""
__author__ = 'ebidel@gmail.com (Eric Bidelman)'
import getopt
import os
import random
import shutil
import sys
import time
import urllib
import urllib2
NO_STATS = 'NO_STATS'
DEFAULT_CLI_NAME = 'yeoman'
NUM_SUB_CMDS = 2 # Subcommand depth. TODO: This assumes only "cmd subcmd" format.
NUM_SECONDS_TO_STASH_DATA = 0 # Send data as it happens.
class Analytics(object):
TRACKING_CODE = 'UA-31537568-1'
BASE_URL = 'http://www.google-analytics.com/collect/__utm.gif'
def __init__(self, tracking_code, application_version):
if not tracking_code:
raise Exception('No tracking code was given')
if not application_version:
raise Exception('No application version was given')
self.tracking_code = tracking_code
self.application_version = application_version
self.do_stats = True
# Create ~/.yeoman/insight if it doesn't exist.
# install.sh should have taken care of it though.
if not os.path.exists(INSIGHT_DIR):
os.makedirs(INSIGHT_DIR)
f = open(LOG_FILE, 'a+') # Open file for reading and appending.
# If we're creating a new file, create a new client ID, setup the file, and
# record the download action. Otherwise, read the existing client ID saved
# on the first line of the file.
if os.path.getsize(LOG_FILE) == 0:
# Record the initial "download/install". Send it right way.
# Then have users opt-in.
self.client_id = '%s%s' % (time.time(), random.random())
self.__reset_file(f, self.client_id)
self.record('downloaded')
self._send_all()
else:
f.seek(0)
self.client_id = f.readline()[:-1] # Assumes the line ends with "\n".
try:
first_entry_timestamp = float(f.readline().split(' ')[0])
time_delta = time.time() - first_entry_timestamp
# If we have data that's too old, send it to Analytics.
if time_delta >= NUM_SECONDS_TO_STASH_DATA:
self._send_all()
except ValueError:
# Error means we tried to parse a non timestamp or one wasn't present.
# That means no stats.
self.do_stats = False
# Insure we're at the EOF to start appending.
f.seek(os.SEEK_END)
f.close()
def __reset_file(self, f, client_id=None):
"""Setups up the log file for writing entries to.
Args:
f: A file object, assumed to be open when passed to this method.
client_id: The client ID to use for this file.
"""
f.write(client_id+ '\n')
f.flush()
def _send(self, path='/', recorded_at=None):
"""Sends one pageview entry to Google Analytics.
This method constructs the appropriate URL and makes a GET request to the
tracking API.
Args:
path: A string representing the url path of the pageview to record.
URL query parameters may be included. The format should map to the
the command that was issued:
yeoman init -> /init
yeoman add model -> /add/model
recorded_at: When the hit was recorded in seconds since the epoch.
If absent, now is used.
Returns:
True if message was sent, otherwise false.
"""
recorded_at = recorded_at or time.time()
params = {
'v': '1', # GA API tracking version.
'tid': self.tracking_code, # Tracking code ID.
't': 'pageview', # Event type
'cid': self.client_id, # Client ID
'aip': '1', # Anonymize IP
'qt': int((time.time() - recorded_at) * 1e3), # Queue Time. Delta (milliseconds) between now and when hit was recorded.
'dp': path,
'an': 'Yeoman Insight', # Application Name.
'av': self.application_version, # Application Version.
'z': time.time() # Cache bust. Probably don't need, but be safe. Should be last param.
}
encoded_params = urllib.urlencode(params)
url = '%s?%s' % (self.BASE_URL, encoded_params)
# Noop if we're offline. Just keep stashing entries.
try:
response = urllib2.urlopen(url)
#print url
#if response.code == 200:
# return True
return True
except urllib2.URLError:
return False
def _send_all(self):
"""Sends all report data stored in the log file to Analytics."""
sent = True
with open(LOG_FILE) as f:
# This assumes every line in the log file ends with "\n".
lines = [line[:-1] for line in f.readlines()]
for l in lines[1:]: # ClientID is always on the first line. Start on 2nd.
parts = l.split(' ')
# If one message fails to send, assume we're offline and bomb out.
sent = self._send(parts[1], recorded_at=float(parts[0]))
if not sent:
break
# Proceed with resetting file if everything went well.
if sent:
# Reset the file by clearing it and adding in the client id.
f = open(LOG_FILE, 'w+')
self.__reset_file(f, self.client_id)
f.close()
def record(self, cmd_str):
"""Saves the command that was run to a log file.
Args:
cmd_str: A string representing the full command that was run.
For example, when running "yeoman add model MyModel", this method
would save "add model" with a timestamp attached.
"""
try:
cmd_str = filter(lambda x: x, cmd_str.split(CLI_NAME))[0].strip()
except IndexError: # yeoman cli cmd was run with no arguments.
cmd_str = ''
path = '/'.join(cmd_str.split(' ')[:NUM_SUB_CMDS])
# yeomaninsight.py ... record NO_STATS was sent from cli on first run.
if self.do_stats and cmd_str != NO_STATS:
f = open(LOG_FILE, 'a')
s = '%s /%s' % (time.time(), path)
f.write(s + '\n')
f.close()
def main(args):
global INSIGHT_DIR
global LOG_FILE
global CLI_NAME
if len(sys.argv) < 2:
print 'Invalid number of arguments.'
sys.exit(1)
try:
opts, args = getopt.getopt(sys.argv[1:], 'v:n:', ['version=', 'name='])
except getopt.GetoptError, err:
print str(err)
sys.exit(1)
application_version = 'x.x.x'
CLI_NAME = DEFAULT_CLI_NAME
for opt, val in opts:
if opt in ('-v', '--version'):
application_version = val
elif opt in ('-n', '--name'):
CLI_NAME = val
method = args[0]
args = ' '.join(args[1:])
# ~/.yeoman/insight
INSIGHT_DIR = os.path.join(os.path.expanduser('~'), '.' + CLI_NAME, 'insight')
LOG_FILE = os.path.join(INSIGHT_DIR, '.log') # ~/.yeoman/insight/.log
ga = Analytics(Analytics.TRACKING_CODE, application_version)
#if callable(getattr(ga, method)):
# getattr(ga, method)()
if method == 'record':
ga.record(args)
#elif method == 'send_all':
# ga.send_all()
if __name__ == '__main__':
main(sys.argv)
Jump to Line
Something went wrong with that request. Please try again.