Files
TimeTracker/scripts/check_audit_logs.py
Dries Peeters 350d7105a2 feat: Add comprehensive audit trail/history tracking system
Implement a complete audit logging system to track all changes made to
tracked entities, providing full compliance and accountability capabilities.

Features:
- Automatic tracking of create, update, and delete operations on 25+ models
- Detailed field-level change tracking with old/new value comparison
- User attribution with IP address, user agent, and request path logging
- Web UI for viewing and filtering audit logs with pagination
- REST API endpoints for programmatic access
- Entity-specific history views
- Comprehensive test coverage (unit, model, route, and smoke tests)

Core Components:
- AuditLog model with JSON-encoded value storage and decoding helpers
- SQLAlchemy event listeners for automatic change detection
- Audit utility module with defensive programming for table existence checks
- Blueprint routes for audit log viewing and API access
- Jinja2 templates for audit log list, detail, and entity history views
- Database migration (044) creating audit_logs table with proper indexes

Technical Implementation:
- Uses SQLAlchemy 'after_flush' event listener to capture changes
- Tracks 25+ models including Projects, Tasks, TimeEntries, Invoices, Clients, Users, etc.
- Excludes sensitive fields (passwords) and system fields (id, timestamps)
- Implements lazy import pattern to avoid circular dependencies
- Graceful error handling to prevent audit logging from breaking core functionality
- Transaction-safe logging that integrates with main application transactions

Fixes:
- Resolved login errors caused by premature transaction commits
- Fixed circular import issues with lazy model loading
- Added table existence checks to prevent errors before migrations
- Improved error handling with debug-level logging for non-critical failures

UI/UX:
- Added "Audit Logs" link to admin dropdown menu
- Organized admin menu into logical sections for better usability
- Filterable audit log views by entity type, user, action, and date range
- Color-coded action badges and side-by-side old/new value display
- Pagination support for large audit log datasets

Documentation:
- Added comprehensive feature documentation
- Included troubleshooting guide and data examples
- Created diagnostic scripts for verifying audit log setup

Testing:
- Unit tests for AuditLog model and value encoding/decoding
- Route tests for all audit log endpoints
- Integration tests for audit logging functionality
- Smoke tests for end-to-end audit trail verification

This implementation provides a robust foundation for compliance tracking
and change accountability without impacting application performance or
requiring code changes in existing routes/models.
2025-11-13 08:08:48 +01:00

78 lines
2.9 KiB
Python

#!/usr/bin/env python
"""Script to check and verify audit_logs table setup"""
import sys
import os
# Add the parent directory to the path so we can import app
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from app import create_app, db
from app.models.audit_log import AuditLog
from sqlalchemy import inspect as sqlalchemy_inspect
def check_audit_table():
"""Check if audit_logs table exists and show status"""
app = create_app()
with app.app_context():
print("=" * 60)
print("Audit Logs Table Check")
print("=" * 60)
# Check if table exists
try:
inspector = sqlalchemy_inspect(db.engine)
tables = inspector.get_table_names()
if 'audit_logs' in tables:
print("✓ audit_logs table EXISTS")
# Check table structure
columns = inspector.get_columns('audit_logs')
print(f"\nTable has {len(columns)} columns:")
for col in columns:
print(f" - {col['name']} ({col['type']})")
# Check indexes
indexes = inspector.get_indexes('audit_logs')
print(f"\nTable has {len(indexes)} indexes:")
for idx in indexes:
print(f" - {idx['name']}: {', '.join(idx['column_names'])}")
# Count existing audit logs
try:
count = AuditLog.query.count()
print(f"\n✓ Current audit log entries: {count}")
if count > 0:
# Show recent entries
recent = AuditLog.query.order_by(AuditLog.created_at.desc()).limit(5).all()
print("\nRecent audit log entries:")
for log in recent:
print(f" - {log.created_at}: {log.action} {log.entity_type}#{log.entity_id} by user#{log.user_id}")
except Exception as e:
print(f"\n⚠ Could not query audit logs: {e}")
print(" This might indicate a schema mismatch.")
else:
print("✗ audit_logs table DOES NOT EXIST")
print("\nTo create the table, run:")
print(" flask db upgrade")
print("\nOr manually apply migration:")
print(" migrations/versions/044_add_audit_logs_table.py")
except Exception as e:
print(f"✗ Error checking table: {e}")
import traceback
traceback.print_exc()
return False
print("\n" + "=" * 60)
return True
if __name__ == '__main__':
success = check_audit_table()
sys.exit(0 if success else 1)