feat: Implement task and reward tracking feature
All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 24s
All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 24s
- Added tracking events for tasks, penalties, and rewards with timestamps. - Created new TinyDB table for tracking records to maintain audit history. - Developed backend API for querying tracking events with filters and pagination. - Implemented logging for tracking events with per-user rotating log files. - Added unit tests for tracking event creation, querying, and anonymization. - Deferred frontend changes for future implementation. - Established acceptance criteria and documentation for the tracking feature. feat: Introduce account deletion scheduler - Implemented a scheduler to delete accounts marked for deletion after a configurable threshold. - Added new fields to the User model to manage deletion status and attempts. - Created admin API endpoints for managing deletion thresholds and viewing the deletion queue. - Integrated error handling and logging for the deletion process. - Developed unit tests for the deletion scheduler and related API endpoints. - Documented the deletion process and acceptance criteria.
This commit is contained in:
84
backend/utils/tracking_logger.py
Normal file
84
backend/utils/tracking_logger.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""Per-user rotating audit logger for tracking events."""
|
||||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from config.paths import get_logs_dir
|
||||
from models.tracking_event import TrackingEvent
|
||||
|
||||
|
||||
# Store handlers per user_id to avoid recreating
|
||||
_user_loggers = {}
|
||||
|
||||
|
||||
def get_tracking_logger(user_id: str) -> logging.Logger:
|
||||
"""
|
||||
Get or create a per-user rotating file logger for tracking events.
|
||||
|
||||
Args:
|
||||
user_id: User ID for the log file
|
||||
|
||||
Returns:
|
||||
Logger instance configured for the user
|
||||
"""
|
||||
if user_id in _user_loggers:
|
||||
return _user_loggers[user_id]
|
||||
|
||||
logs_dir = get_logs_dir()
|
||||
os.makedirs(logs_dir, exist_ok=True)
|
||||
|
||||
log_file = os.path.join(logs_dir, f'tracking_user_{user_id}.log')
|
||||
|
||||
logger = logging.getLogger(f'tracking.user.{user_id}')
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.propagate = False # Don't propagate to root logger
|
||||
|
||||
# Rotating file handler: 10MB max, keep 5 backups
|
||||
handler = RotatingFileHandler(
|
||||
log_file,
|
||||
maxBytes=10 * 1024 * 1024, # 10MB
|
||||
backupCount=5,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s | %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(handler)
|
||||
_user_loggers[user_id] = logger
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
def log_tracking_event(event: TrackingEvent) -> None:
|
||||
"""
|
||||
Log a tracking event to the user's audit log file.
|
||||
|
||||
Args:
|
||||
event: TrackingEvent to log
|
||||
"""
|
||||
if not event.user_id:
|
||||
# If user was deleted (anonymized), skip logging
|
||||
return
|
||||
|
||||
logger = get_tracking_logger(event.user_id)
|
||||
|
||||
log_msg = (
|
||||
f"user_id={event.user_id} | "
|
||||
f"child_id={event.child_id} | "
|
||||
f"entity_type={event.entity_type} | "
|
||||
f"entity_id={event.entity_id} | "
|
||||
f"action={event.action} | "
|
||||
f"points_before={event.points_before} | "
|
||||
f"points_after={event.points_after} | "
|
||||
f"delta={event.delta:+d} | "
|
||||
f"occurred_at={event.occurred_at}"
|
||||
)
|
||||
|
||||
if event.metadata:
|
||||
metadata_str = ' | '.join(f"{k}={v}" for k, v in event.metadata.items())
|
||||
log_msg += f" | {metadata_str}"
|
||||
|
||||
logger.info(log_msg)
|
||||
Reference in New Issue
Block a user