feat: add user engagement tracking (login_count, last_active_at, page_views_count)
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ace9c15cd7
commit
825c79c399
17
app.py
17
app.py
@ -617,10 +617,12 @@ def check_geoip():
|
||||
|
||||
@app.before_request
|
||||
def update_last_active():
|
||||
"""Update last_login periodically (every 5 min) so profile shows fresh activity."""
|
||||
"""Update last_active_at and page_views_count for engagement tracking."""
|
||||
if not current_user.is_authenticated:
|
||||
return
|
||||
if request.path.startswith('/static') or request.path == '/health':
|
||||
if request.path.startswith('/static') or request.path == '/health' or request.path == '/favicon.ico':
|
||||
return
|
||||
if request.path.startswith('/api'):
|
||||
return
|
||||
from flask import session as flask_session
|
||||
import time
|
||||
@ -629,11 +631,15 @@ def update_last_active():
|
||||
if now - last_update < 300: # 5 minutes
|
||||
return
|
||||
flask_session['_last_active_update'] = now
|
||||
# Count page views in session, flush to DB every 5 min
|
||||
pv = flask_session.get('_pv_buffer', 0)
|
||||
flask_session['_pv_buffer'] = 0
|
||||
try:
|
||||
db = SessionLocal()
|
||||
user = db.query(User).filter_by(id=current_user.id).first()
|
||||
if user:
|
||||
user.last_login = datetime.now()
|
||||
user.last_active_at = datetime.now()
|
||||
user.page_views_count = (user.page_views_count or 0) + pv
|
||||
db.commit()
|
||||
db.close()
|
||||
except Exception:
|
||||
@ -663,6 +669,11 @@ def track_page_view():
|
||||
if request.path == '/favicon.ico':
|
||||
return
|
||||
|
||||
# Buffer page views for authenticated users (flushed in update_last_active)
|
||||
if current_user.is_authenticated:
|
||||
from flask import session as flask_session
|
||||
flask_session['_pv_buffer'] = flask_session.get('_pv_buffer', 0) + 1
|
||||
|
||||
try:
|
||||
session_db_id = get_or_create_analytics_session()
|
||||
if not session_db_id:
|
||||
|
||||
@ -380,6 +380,7 @@ def login():
|
||||
# No 2FA - login directly
|
||||
login_user(user, remember=remember)
|
||||
user.last_login = datetime.now()
|
||||
user.login_count = (user.login_count or 0) + 1
|
||||
_auto_link_person(db, user)
|
||||
|
||||
# Log successful login to audit
|
||||
@ -478,6 +479,7 @@ def verify_2fa():
|
||||
login_user(user, remember=remember)
|
||||
session['2fa_verified'] = True
|
||||
user.last_login = datetime.now()
|
||||
user.login_count = (user.login_count or 0) + 1
|
||||
_auto_link_person(db, user)
|
||||
|
||||
# Log successful 2FA login to audit
|
||||
@ -1251,6 +1253,7 @@ def verify_email(token):
|
||||
if not current_user.is_authenticated:
|
||||
login_user(user, remember=True)
|
||||
user.last_login = datetime.now()
|
||||
user.login_count = (user.login_count or 0) + 1
|
||||
db.commit()
|
||||
flash('Witamy ponownie! Zostales automatycznie zalogowany.', 'info')
|
||||
return redirect(url_for('dashboard'))
|
||||
@ -1265,6 +1268,7 @@ def verify_email(token):
|
||||
# Auto-login the user after verification
|
||||
login_user(user, remember=True)
|
||||
user.last_login = datetime.now()
|
||||
user.login_count = (user.login_count or 0) + 1
|
||||
|
||||
# Log successful verification and login
|
||||
client_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||
|
||||
@ -298,8 +298,13 @@ class User(Base, UserMixin):
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.now)
|
||||
last_login = Column(DateTime)
|
||||
last_active_at = Column(DateTime)
|
||||
verified_at = Column(DateTime)
|
||||
|
||||
# Engagement metrics
|
||||
login_count = Column(Integer, default=0)
|
||||
page_views_count = Column(Integer, default=0)
|
||||
|
||||
# Verification token
|
||||
verification_token = Column(String(255))
|
||||
verification_token_expires = Column(DateTime)
|
||||
|
||||
20
database/migrations/add_user_engagement_metrics.sql
Normal file
20
database/migrations/add_user_engagement_metrics.sql
Normal file
@ -0,0 +1,20 @@
|
||||
-- Add user engagement tracking columns
|
||||
-- 2026-03-13
|
||||
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS login_count INTEGER DEFAULT 0;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS last_active_at TIMESTAMP;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS page_views_count INTEGER DEFAULT 0;
|
||||
|
||||
-- Backfill login_count from audit_logs
|
||||
UPDATE users u
|
||||
SET login_count = COALESCE(sub.cnt, 0)
|
||||
FROM (
|
||||
SELECT user_id, COUNT(*) as cnt
|
||||
FROM audit_logs
|
||||
WHERE action = 'login'
|
||||
GROUP BY user_id
|
||||
) sub
|
||||
WHERE u.id = sub.user_id;
|
||||
|
||||
-- Backfill last_active_at from last_login
|
||||
UPDATE users SET last_active_at = last_login WHERE last_login IS NOT NULL;
|
||||
Loading…
Reference in New Issue
Block a user