├── .gitignore ├── flask_analytics.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | *.pyc 3 | 4 | # Git 5 | *.orig 6 | *.rej 7 | 8 | # Vim 9 | .*.swp 10 | .*.swo 11 | .*.swp 12 | 13 | # Emacs 14 | *~ 15 | 16 | # distutil 17 | MANIFEST 18 | dist/* 19 | build/* 20 | *.egg-info 21 | -------------------------------------------------------------------------------- /flask_analytics.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from datetime import datetime, timedelta 4 | from hashlib import sha1 5 | from urlparse import urlparse 6 | from uuid import uuid4 7 | import hmac 8 | 9 | from flask import _request_ctx_stack, current_app, session 10 | 11 | COOKIE_NAME = 'analytics' 12 | COOKIE_DURATION = timedelta(days=365) 13 | 14 | 15 | def _cookie_digest(payload, key=None): 16 | if key is None: 17 | key = current_app.config["SECRET_KEY"] 18 | payload = payload.encode("utf8") 19 | mac = hmac.new(key, payload, sha1) 20 | return mac.hexdigest() 21 | 22 | 23 | def _get_cookie(request): 24 | config = current_app.config 25 | cookie_name = config.get("ANALYTICS_COOKIE_NAME", COOKIE_NAME) 26 | request_cookie = request.headers['cookie'] 27 | cookies = request_cookie.split(';') 28 | for cookie_data in cookies: 29 | if cookie_name in cookie_data: 30 | name, value = cookie_data.split('=') 31 | return value 32 | return None 33 | 34 | 35 | class Analytics(object): 36 | 37 | analytics_callback = None 38 | 39 | def __init__(self, app): 40 | self.app = app 41 | self.cookie_value = None 42 | if self.app: 43 | self.init_app(app) 44 | 45 | def init_app(self, app): 46 | self.app = app 47 | if not hasattr(app, 'extensions'): 48 | app.extensions = {} 49 | app.extensions['analytics'] = self 50 | app.before_request(self.before_request) 51 | app.after_request(self.after_request) 52 | 53 | def analytics_process(self, callback): 54 | self.analytics_callback = callback 55 | 56 | def before_request(self): 57 | ctx = _request_ctx_stack.top 58 | cookie_value = _get_cookie(ctx.request) 59 | if cookie_value: 60 | self.cookie_value = cookie_value 61 | self.track_request(ctx.request) 62 | 63 | def after_request(self, response): 64 | config = current_app.config 65 | cookie_name = config.get("ANALYTICS_COOKIE_NAME", COOKIE_NAME) 66 | self._set_tracker(response) 67 | return response 68 | 69 | def _set_tracker(self, response): 70 | if self.cookie_value: 71 | return 72 | config = current_app.config 73 | name = config.get("ANALYTICS_COOKIE_NAME", COOKIE_NAME) 74 | domain = config.get("ANALYTICS_COOKIE_DOMAIN", None) 75 | duration = config.get("ANALYTICS_COOKIE_DURATION", COOKIE_DURATION) 76 | data = _cookie_digest(str(uuid4())) 77 | expires = datetime.now() + duration 78 | response.set_cookie(name, data, expires=expires, domain=domain) 79 | 80 | def track_request(self, request): 81 | parse_result = urlparse(request.url) 82 | static_url_path = current_app.static_url_path 83 | analytics = { 84 | 'args': request.args, 85 | 'charset': request.url_charset, 86 | 'url': request.url, 87 | 'user_agent': request.user_agent, 88 | 'cookie': self.cookie_value, 89 | 'is_static': parse_result.path.startswith(static_url_path), 90 | 'blueprint': request.blueprint, 91 | 'view_args': request.view_args, 92 | 'remote_addr': request.remote_addr, 93 | } 94 | self.analytics_callback(analytics) 95 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Flask-Analytics 3 | ------------- 4 | 5 | Monitor users navigation history. 6 | """ 7 | from setuptools import setup 8 | 9 | version='0.1' 10 | 11 | 12 | setup( 13 | name='Flask-Analytics', 14 | version=version, 15 | license='BSD', 16 | author='Fabien Reboia', 17 | author_email='srounet@gmail.com', 18 | description='Monitor users navigation history', 19 | long_description=__doc__, 20 | py_modules=['flask_analytics'], 21 | zip_safe=False, 22 | platforms='any', 23 | install_requires=[ 24 | 'Flask' 25 | ], 26 | classifiers=[ 27 | 'Environment :: Web Environment', 28 | 'Intended Audience :: Developers', 29 | 'License :: OSI Approved :: BSD License', 30 | 'Operating System :: OS Independent', 31 | 'Programming Language :: Python', 32 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 33 | 'Topic :: Software Development :: Libraries :: Python Modules' 34 | ] 35 | ) 36 | --------------------------------------------------------------------------------