Flask Extension
Flask extension with decorators and middleware
Flask Extension
The entrolytics-flask-middleware package provides a Flask extension for automatic analytics tracking with decorators, middleware, and seamless Flask integration.
Installation
pip install entrolytics-flask-middlewareQuick Start
Basic Setup
from flask import Flask
from entrolytics_flask import Entrolytics
app = Flask(__name__)
# Initialize Entrolytics
entrolytics = Entrolytics(app,
website_id='your-website-id',
api_key='your-api-key'
)
@app.route('/')
def home():
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True)Factory Pattern
from flask import Flask
from entrolytics_flask import Entrolytics
def create_app():
app = Flask(__name__)
# Configure Entrolytics
app.config['ENTROLYTICS_WEBSITE_ID'] = 'your-website-id'
app.config['ENTROLYTICS_API_KEY'] = 'your-api-key'
# Initialize extension
entrolytics = Entrolytics(app)
return app
app = create_app()Decorator Usage
from flask import Flask
from entrolytics_flask import Entrolytics, track_event, track_page
app = Flask(__name__)
entrolytics = Entrolytics(app, website_id='your-website-id')
@app.route('/users/<int:user_id>')
@track_page('user_profile')
def user_profile(user_id):
user = get_user(user_id)
return render_template('profile.html', user=user)
@app.route('/api/users', methods=['POST'])
@track_event('user_creation_attempt')
def create_user():
user_data = request.json
try:
user = create_user_in_db(user_data)
# Track success event
entrolytics.track('user_created', {
'user_id': user.id,
'email': user.email
})
return jsonify({'user_id': user.id}), 201
except Exception as e:
# Track error
entrolytics.track('user_creation_failed', {
'error': str(e),
'email': user_data.get('email')
})
raiseConfiguration
Extension Options
from flask import Flask
from entrolytics_flask import Entrolytics
app = Flask(__name__)
# Direct initialization
entrolytics = Entrolytics(app,
website_id='your-website-id', # required
api_key='your-api-key', # optional
host='https://entrolytics.dev', # custom host
auto_track=True, # auto-track requests
track_errors=True, # track exceptions
track_forms=True, # track form submissions
exclude_patterns=[r'/health', r'/static/.*'], # exclude paths
include_headers=['User-Agent', 'X-Forwarded-For'], # include headers
debug=False # debug mode
)Configuration via Flask Config
# config.py
class Config:
ENTROLYTICS_WEBSITE_ID = 'your-website-id'
ENTROLYTICS_API_KEY = 'your-api-key'
ENTROLYTICS_HOST = 'https://entrolytics.dev'
ENTROLYTICS_AUTO_TRACK = True
ENTROLYTICS_TRACK_ERRORS = True
ENTROLYTICS_TRACK_FORMS = True
ENTROLYTICS_EXCLUDE_PATTERNS = [r'/health', r'/static/.*']
ENTROLYTICS_INCLUDE_HEADERS = ['User-Agent', 'X-Forwarded-For']
ENTROLYTICS_DEBUG = False
class DevelopmentConfig(Config):
ENTROLYTICS_DEBUG = True
class ProductionConfig(Config):
ENTROLYTICS_DEBUG = FalseEnvironment Variables
# .env
ENTROLYTICS_WEBSITE_ID=your-website-id
ENTROLYTICS_API_KEY=your-api-key
ENTROLYTICS_HOST=https://entrolytics.dev
ENTROLYTICS_AUTO_TRACK=true
ENTROLYTICS_TRACK_ERRORS=true
ENTROLYTICS_DEBUG=falseAPI Reference
Entrolytics Extension
class Entrolytics:
def __init__(self, app=None, **kwargs)
def init_app(self, app, **kwargs)
def track(self, event, properties=None, user_id=None, session_id=None)
def track_page(self, url=None, properties=None, user_id=None, session_id=None)
def identify(self, user_id, traits=None)
def set_user_properties(self, properties)
def track_batch(self, events)
def flush(self)
def set_enabled(self, enabled)
def is_enabled(self)
def get_config(self)Decorators
@track_event(event_name, properties=None)
def view_function(*args, **kwargs):
pass
@track_page(page_name, properties=None)
def view_function(*args, **kwargs):
pass
@identify_user(user_id_func=None, traits_func=None)
def view_function(*args, **kwargs):
pass
@track_form(form_name=None, properties_func=None)
def view_function(*args, **kwargs):
passRequest Context
# In Flask routes
from flask import request
# Access entrolytics via request context
request.entrolytics.track('custom_event', {'property': 'value'})
request.entrolytics.identify('user-123', {'email': 'user@example.com'})
request.entrolytics.page('/custom-page', {'title': 'Custom Page'})
# Get current user/session info
user_id = request.entrolytics.get_user_id()
session_id = request.entrolytics.get_session_id()Advanced Usage
Custom Middleware
from flask import Flask, request, g
from entrolytics_flask import Entrolytics
app = Flask(__name__)
entrolytics = Entrolytics(app)
@app.before_request
def track_request_info():
# Track API version
api_version = request.headers.get('API-Version')
if api_version:
request.entrolytics.track('api_request', {
'version': api_version,
'method': request.method,
'endpoint': request.endpoint
})
# Track authenticated users
if hasattr(g, 'user') and g.user:
request.entrolytics.identify(g.user.id, {
'email': g.user.email,
'role': g.user.role
})
@app.after_request
def track_response_info(response):
# Track response status
if response.status_code >= 400:
request.entrolytics.track('http_error', {
'status_code': response.status_code,
'url': request.url,
'method': request.method
})
return responseForm Tracking
from flask import Flask, request, render_template, redirect, url_for
from entrolytics_flask import Entrolytics, track_form
app = Flask(__name__)
entrolytics = Entrolytics(app, track_forms=True)
@app.route('/contact', methods=['GET', 'POST'])
@track_form('contact_form')
def contact():
if request.method == 'POST':
# Form submission is automatically tracked
name = request.form.get('name')
email = request.form.get('email')
message = request.form.get('message')
# Track custom form properties
request.entrolytics.track('contact_submission', {
'name_length': len(name),
'has_message': len(message) > 0,
'source': request.form.get('source', 'direct')
})
# Process form...
return redirect(url_for('thank_you'))
return render_template('contact.html')
# Custom form tracking
@app.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
# Track form with custom properties
form_data = {
'email': request.form.get('email'),
'plan': request.form.get('plan', 'free'),
'source': request.form.get('source')
}
request.entrolytics.track('signup_form_submission', form_data)
# Process signup...
return redirect(url_for('welcome'))
return render_template('signup.html')User Authentication Integration
from flask import Flask, session, request, g
from entrolytics_flask import Entrolytics, identify_user
app = Flask(__name__)
app.secret_key = 'your-secret-key'
entrolytics = Entrolytics(app)
@app.before_request
def load_user():
if 'user_id' in session:
g.user = get_user_by_id(session['user_id'])
@app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
password = request.form.get('password')
user = authenticate_user(username, password)
if user:
session['user_id'] = user.id
# Track login success
request.entrolytics.identify(user.id, {
'username': user.username,
'email': user.email,
'role': user.role
})
request.entrolytics.track('login_success', {
'method': 'form',
'username': username
})
return redirect(url_for('dashboard'))
else:
# Track login failure
request.entrolytics.track('login_failed', {
'username': username,
'reason': 'invalid_credentials'
})
return render_template('login.html', error='Invalid credentials')
@app.route('/logout')
def logout():
user_id = session.get('user_id')
# Track logout
request.entrolytics.track('logout', {
'user_id': user_id,
'method': 'explicit'
})
session.clear()
return redirect(url_for('home'))API Integration
from flask import Flask, request, jsonify
from entrolytics_flask import Entrolytics
app = Flask(__name__)
entrolytics = Entrolytics(app)
@app.route('/api/users', methods=['POST'])
def create_user():
user_data = request.json
# Track API call
request.entrolytics.track('api_call', {
'endpoint': '/api/users',
'method': 'POST',
'user_agent': request.headers.get('User-Agent'),
'content_type': request.content_type
})
try:
user = create_user_in_database(user_data)
# Track success
request.entrolytics.track('user_created', {
'user_id': user.id,
'email': user.email,
'source': 'api'
})
return jsonify({
'user_id': user.id,
'email': user.email
}), 201
except ValidationError as e:
# Track validation error
request.entrolytics.track('validation_error', {
'endpoint': '/api/users',
'errors': e.errors,
'invalid_fields': list(e.errors.keys())
})
return jsonify({'errors': e.errors}), 400
except Exception as e:
# Track server error
request.entrolytics.track('server_error', {
'endpoint': '/api/users',
'error': str(e),
'error_type': type(e).__name__
})
raise
# API middleware for authentication
@app.before_request
def api_auth():
if request.path.startswith('/api/'):
token = request.headers.get('Authorization', '').replace('Bearer ', '')
if token:
try:
user = decode_jwt_token(token)
g.user = user
# Identify API user
request.entrolytics.identify(user.id, {
'email': user.email,
'api_key': token[:10] + '...',
'permissions': user.permissions
})
request.entrolytics.track('api_authenticated', {
'endpoint': request.endpoint,
'method': request.method
})
except InvalidTokenError:
request.entrolytics.track('api_auth_failed', {
'endpoint': request.endpoint,
'token': token[:10] + '...'
})Error Tracking
from flask import Flask, request
from entrolytics_flask import Entrolytics
app = Flask(__name__)
entrolytics = Entrolytics(app, track_errors=True)
@app.errorhandler(404)
def not_found(error):
request.entrolytics.track('404_error', {
'url': request.url,
'method': request.method,
'user_agent': request.headers.get('User-Agent'),
'referrer': request.referrer
})
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
request.entrolytics.track('500_error', {
'url': request.url,
'method': request.method,
'error_message': str(error),
'user_id': getattr(request, 'user_id', None)
})
return render_template('500.html'), 500
# Custom error tracking
@app.route('/risky-operation')
def risky_operation():
try:
result = perform_risky_operation()
return result
except SpecificError as e:
request.entrolytics.track('specific_error', {
'operation': 'risky_operation',
'error_code': e.code,
'error_message': e.message,
'context': e.context
})
raise
except Exception as e:
request.entrolytics.track('unexpected_error', {
'operation': 'risky_operation',
'error_type': type(e).__name__,
'error_message': str(e)
})
raiseTesting
Unit Tests
import pytest
from flask import Flask
from entrolytics_flask import Entrolytics
from unittest.mock import Mock, patch
@pytest.fixture
def app():
app = Flask(__name__)
app.config['TESTING'] = True
app.config['ENTROLYTICS_WEBSITE_ID'] = 'test-website-id'
with patch('entrolytics_flask.client.EntrolyticsClient') as mock_client:
mock_instance = Mock()
mock_client.return_value = mock_instance
entrolytics = Entrolytics(app)
entrolytics_mock = mock_instance
yield app
@pytest.fixture
def client(app):
return app.test_client()
def test_basic_tracking(app, client):
with app.test_request_context('/'):
entrolytics_mock.track.assert_not_called()
# Make request
response = client.get('/')
# Check if automatic tracking was called
entrolytics_mock.track.assert_called()
def test_decorator_tracking(app, client):
@app.route('/test')
def test_route():
return 'test'
with app.test_request_context('/test'):
from entrolytics_flask import track_event
@track_event('test_event')
def decorated_view():
return 'test'
# Test decorator
response = client.get('/test')
entrolytics_mock.track.assert_called_with('test_event', None, None, None)Integration Tests
import pytest
from flask import Flask
from entrolytics_flask import Entrolytics
def test_full_integration():
app = Flask(__name__)
app.config['ENTROLYTICS_WEBSITE_ID'] = 'integration-test-id'
# Use real client for integration testing
entrolytics = Entrolytics(app, debug=True)
@app.route('/test')
def test_route():
request.entrolytics.track('integration_test', {'test': True})
return 'test'
with app.test_client() as client:
response = client.get('/test')
# Verify response
assert response.status_code == 200
assert response.data == b'test'
# In real integration, you would verify
# that events were actually sent to the APIPerformance Optimization
Async Operations
import asyncio
from concurrent.futures import ThreadPoolExecutor
from flask import Flask
from entrolytics_flask import Entrolytics
app = Flask(__name__)
entrolytics = Entrolytics(app)
# Background thread for async operations
executor = ThreadPoolExecutor(max_workers=2)
def track_background(event, properties):
"""Track events in background thread"""
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
# Use async client in background
client = get_async_client()
loop.run_until_complete(client.track(event, properties))
finally:
loop.close()
@app.route('/heavy-operation')
def heavy_operation():
# Track event without blocking
executor.submit(track_background, 'heavy_operation_start', {
'timestamp': time.time()
})
# Perform heavy operation
result = perform_heavy_operation()
# Track completion
executor.submit(track_background, 'heavy_operation_complete', {
'result_size': len(result)
})
return resultBatching
from flask import Flask
from entrolytics_flask import Entrolytics
import threading
import time
app = Flask(__name__)
entrolytics = Entrolytics(app)
# Global event batch
event_batch = []
batch_lock = threading.Lock()
def flush_batch():
"""Flush events in background"""
while True:
time.sleep(30) # Flush every 30 seconds
with batch_lock:
if event_batch:
# Send batch
entrolytics.track_batch(event_batch.copy())
event_batch.clear()
# Start background thread
batch_thread = threading.Thread(target=flush_batch, daemon=True)
batch_thread.start()
@app.route('/track-batch')
def track_batch_endpoint():
with batch_lock:
event_batch.append({
'event': 'batch_test',
'properties': {'timestamp': time.time()}
})
return 'Event added to batch'Troubleshooting
Best Practices
Migration Guide
From Manual Analytics
# Before - Manual tracking
import analytics
@app.route('/old-way')
def old_way():
analytics.track('page_view', {'url': '/old-way'})
return 'old'
# After - Flask extension
from entrolytics_flask import Entrolytics, track_page
app = Flask(__name__)
entrolytics = Entrolytics(app)
@app.route('/new-way')
@track_page('new_page')
def new_way():
return 'new'From Other Flask Analytics
# Before - Other analytics
from flask_analytics import Analytics
app = Flask(__name__)
analytics = Analytics(app)
# After - Entrolytics
from entrolytics_flask import Entrolytics
app = Flask(__name__)
entrolytics = Entrolytics(app)Flask extension for Entrolytics - First-party growth analytics for the edge