mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-02-06 20:28:45 -06:00
This commit introduces several high-impact features to improve user experience and productivity: New Features: - Activity Logging: Comprehensive audit trail tracking user actions across the system with Activity model, including IP address and user agent tracking - Time Entry Templates: Reusable templates for frequently logged activities with usage tracking and quick-start functionality - Saved Filters: Save and reuse common search/filter combinations across different views (projects, tasks, reports) - User Preferences: Enhanced user settings including email notifications, timezone, date/time formats, week start day, and theme preferences - Excel Export: Generate formatted Excel exports for time entries and reports with styling and proper formatting - Email Notifications: Complete email system for task assignments, overdue invoices, comments, and weekly summaries with HTML templates - Scheduled Tasks: Background task scheduler for periodic operations Models Added: - Activity: Tracks all user actions with detailed context and metadata - TimeEntryTemplate: Stores reusable time entry configurations - SavedFilter: Manages user-saved filter configurations Routes Added: - user.py: User profile and settings management - saved_filters.py: CRUD operations for saved filters - time_entry_templates.py: Template management endpoints UI Enhancements: - Bulk actions widget component - Keyboard shortcuts help modal with advanced shortcuts - Save filter widget component - Email notification templates - User profile and settings pages - Saved filters management interface - Time entry templates interface Database Changes: - Migration 022: Creates activities and time_entry_templates tables - Adds user preference columns (notifications, timezone, date/time formats) - Proper indexes for query optimization Backend Updates: - Enhanced keyboard shortcuts system (commands.js, keyboard-shortcuts-advanced.js) - Updated projects, reports, and tasks routes with activity logging - Safe database commit utilities integration - Event tracking for analytics Dependencies: - Added openpyxl for Excel generation - Added Flask-Mail dependencies - Updated requirements.txt All new features include proper error handling, activity logging integration, and maintain existing functionality while adding new capabilities.
180 lines
6.5 KiB
Python
180 lines
6.5 KiB
Python
"""Scheduled background tasks for the application"""
|
|
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
from flask import current_app
|
|
from app import db
|
|
from app.models import Invoice, User, TimeEntry
|
|
from app.utils.email import send_overdue_invoice_notification, send_weekly_summary
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def check_overdue_invoices():
|
|
"""Check for overdue invoices and send notifications
|
|
|
|
This task should be run daily to check for invoices that are past their due date
|
|
and send notifications to users who have overdue invoice notifications enabled.
|
|
"""
|
|
try:
|
|
logger.info("Checking for overdue invoices...")
|
|
|
|
# Get all invoices that are overdue and not paid/cancelled
|
|
today = datetime.utcnow().date()
|
|
overdue_invoices = Invoice.query.filter(
|
|
Invoice.due_date < today,
|
|
Invoice.status.in_(['draft', 'sent'])
|
|
).all()
|
|
|
|
logger.info(f"Found {len(overdue_invoices)} overdue invoices")
|
|
|
|
notifications_sent = 0
|
|
for invoice in overdue_invoices:
|
|
# Update invoice status to overdue if it's not already
|
|
if invoice.status != 'overdue':
|
|
invoice.status = 'overdue'
|
|
db.session.commit()
|
|
|
|
# Get users to notify (creator and admins)
|
|
users_to_notify = set()
|
|
|
|
# Add the invoice creator
|
|
if invoice.creator:
|
|
users_to_notify.add(invoice.creator)
|
|
|
|
# Add all admins
|
|
admins = User.query.filter_by(role='admin', is_active=True).all()
|
|
users_to_notify.update(admins)
|
|
|
|
# Send notifications
|
|
for user in users_to_notify:
|
|
if user.email and user.email_notifications and user.notification_overdue_invoices:
|
|
try:
|
|
send_overdue_invoice_notification(invoice, user)
|
|
notifications_sent += 1
|
|
logger.info(f"Sent overdue notification for invoice {invoice.invoice_number} to {user.username}")
|
|
except Exception as e:
|
|
logger.error(f"Failed to send notification to {user.username}: {e}")
|
|
|
|
logger.info(f"Sent {notifications_sent} overdue invoice notifications")
|
|
return notifications_sent
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error checking overdue invoices: {e}")
|
|
return 0
|
|
|
|
|
|
def send_weekly_summaries():
|
|
"""Send weekly time tracking summaries to users
|
|
|
|
This task should be run weekly (e.g., Sunday evening or Monday morning)
|
|
to send time tracking summaries to users who have opted in.
|
|
"""
|
|
try:
|
|
logger.info("Sending weekly summaries...")
|
|
|
|
# Get users who want weekly summaries
|
|
users = User.query.filter_by(
|
|
is_active=True,
|
|
email_notifications=True,
|
|
notification_weekly_summary=True
|
|
).all()
|
|
|
|
logger.info(f"Found {len(users)} users with weekly summaries enabled")
|
|
|
|
# Calculate date range (last 7 days)
|
|
end_date = datetime.utcnow().date()
|
|
start_date = end_date - timedelta(days=7)
|
|
|
|
summaries_sent = 0
|
|
for user in users:
|
|
if not user.email:
|
|
continue
|
|
|
|
try:
|
|
# Get time entries for this user in the past week
|
|
entries = TimeEntry.query.filter(
|
|
TimeEntry.user_id == user.id,
|
|
TimeEntry.start_time >= datetime.combine(start_date, datetime.min.time()),
|
|
TimeEntry.start_time < datetime.combine(end_date + timedelta(days=1), datetime.min.time()),
|
|
TimeEntry.end_time.isnot(None)
|
|
).all()
|
|
|
|
if not entries:
|
|
logger.info(f"No entries for {user.username}, skipping")
|
|
continue
|
|
|
|
# Calculate hours worked
|
|
hours_worked = sum(e.duration_hours for e in entries)
|
|
|
|
# Group by project
|
|
projects_map = {}
|
|
for entry in entries:
|
|
if entry.project:
|
|
project_name = entry.project.name
|
|
if project_name not in projects_map:
|
|
projects_map[project_name] = {'name': project_name, 'hours': 0}
|
|
projects_map[project_name]['hours'] += entry.duration_hours
|
|
|
|
projects_data = sorted(projects_map.values(), key=lambda x: x['hours'], reverse=True)
|
|
|
|
# Send email
|
|
send_weekly_summary(
|
|
user=user,
|
|
start_date=start_date.strftime('%Y-%m-%d'),
|
|
end_date=end_date.strftime('%Y-%m-%d'),
|
|
hours_worked=hours_worked,
|
|
projects_data=projects_data
|
|
)
|
|
|
|
summaries_sent += 1
|
|
logger.info(f"Sent weekly summary to {user.username}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to send weekly summary to {user.username}: {e}")
|
|
|
|
logger.info(f"Sent {summaries_sent} weekly summaries")
|
|
return summaries_sent
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error sending weekly summaries: {e}")
|
|
return 0
|
|
|
|
|
|
def register_scheduled_tasks(scheduler):
|
|
"""Register all scheduled tasks with APScheduler
|
|
|
|
Args:
|
|
scheduler: APScheduler instance
|
|
"""
|
|
try:
|
|
# Check overdue invoices daily at 9 AM
|
|
scheduler.add_job(
|
|
func=check_overdue_invoices,
|
|
trigger='cron',
|
|
hour=9,
|
|
minute=0,
|
|
id='check_overdue_invoices',
|
|
name='Check for overdue invoices',
|
|
replace_existing=True
|
|
)
|
|
logger.info("Registered overdue invoices check task")
|
|
|
|
# Send weekly summaries every Monday at 8 AM
|
|
scheduler.add_job(
|
|
func=send_weekly_summaries,
|
|
trigger='cron',
|
|
day_of_week='mon',
|
|
hour=8,
|
|
minute=0,
|
|
id='send_weekly_summaries',
|
|
name='Send weekly time summaries',
|
|
replace_existing=True
|
|
)
|
|
logger.info("Registered weekly summaries task")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error registering scheduled tasks: {e}")
|
|
|