#!/usr/bin/env python3 """ Generate team-wide summaries (daily, weekly, monthly) This can run via cron at different intervals """ import sys import argparse from datetime import datetime, timedelta from config_multi_user import validate_config, setup_logging, TIMEZONE, SUMMARIES_DIR from data_handler_team import get_team_handler def format_duration(minutes): """Format minutes into readable string""" hours = minutes // 60 mins = minutes % 60 return f"{hours}h {mins}m" if hours > 0 else f"{mins}m" def print_daily_summary(team_summary): """Print formatted daily team summary""" if not team_summary: print("No data available for this date.") return print("\n" + "=" * 80) print(f"TEAM DAILY SUMMARY - {team_summary['date']}") print("=" * 80) # Team overview print(f"\nTeam Overview:") print(f" Total team members tracked: {team_summary['team_totals']['total_users']}") print(f" Active members: {team_summary['team_totals']['active_users']}") print(f" Total team hours: {team_summary['team_totals']['total_hours']:.2f}h") print(f" Average hours per active member: {team_summary['team_totals']['average_hours']:.2f}h") print(f" Earliest start: {team_summary['team_totals']['earliest_start'] or 'N/A'}") print(f" Latest end: {team_summary['team_totals']['latest_end'] or 'N/A'}") # Individual performance print("\nIndividual Summary:") print("-" * 60) print(f"{'Name':<20} {'Hours':<10} {'First Seen':<12} {'Last Seen':<12}") print("-" * 60) # Sort users by total hours sorted_users = sorted(team_summary['users'].items(), key=lambda x: x[1]['total_hours'], reverse=True) for user_id, user_data in sorted_users: name = user_data['name'][:19] # Truncate long names hours = f"{user_data['total_hours']:.2f}h" first = user_data['first_seen'] or '-' last = user_data['last_seen'] or '-' print(f"{name:<20} {hours:<10} {first:<12} {last:<12}") # Hourly distribution print("\nTeam Presence by Hour (number of active users):") print("-" * 60) for hour in range(24): active_users = team_summary['hourly_team_presence'][hour] if active_users > 0: bar = '█' * active_users print(f"{hour:02d}:00 {active_users:2d} {bar}") print("=" * 80) def print_weekly_summary(week_data): """Print formatted weekly team summary""" if not week_data: print("No weekly data available.") return print("\n" + "=" * 80) print(f"TEAM WEEKLY SUMMARY - {week_data['week_start']} to {week_data['week_end']}") print("=" * 80) # Weekly overview print(f"\nWeekly Overview:") print(f" Total team hours: {week_data['team_totals']['total_hours']:.2f}h") print(f" Average active users per day: {week_data['team_totals']['average_daily_active']:.1f}") print(f" Most active day: {week_data['team_totals']['most_active_day'] or 'N/A'}") print(f" Least active day: {week_data['team_totals']['least_active_day'] or 'N/A'}") # Daily breakdown print("\nDaily Active Users:") for day, count in week_data['daily_active_users'].items(): bar = '█' * count print(f" {day:<10} {count:2d} {bar}") # Individual weekly performance print("\nIndividual Weekly Summary:") print("-" * 70) print(f"{'Name':<20} {'Total Hours':<12} {'Days Active':<12} {'Avg/Day':<10}") print("-" * 70) # Sort users by total hours sorted_users = sorted(week_data['users'].items(), key=lambda x: x[1]['total_hours'], reverse=True) for user_id, user_data in sorted_users: if user_data['total_hours'] > 0: name = user_data['name'][:19] total = f"{user_data['total_hours']:.2f}h" days = f"{user_data['days_active']}/7" avg = f"{user_data['average_hours_per_day']:.2f}h" print(f"{name:<20} {total:<12} {days:<12} {avg:<10}") print("=" * 80) def print_monthly_summary(month_data): """Print formatted monthly team summary""" if not month_data: print("No monthly data available.") return print("\n" + "=" * 80) print(f"TEAM MONTHLY SUMMARY - {month_data['month']}") print("=" * 80) # Monthly overview print(f"\nMonthly Overview:") print(f" Work days in month: {month_data['team_totals']['total_work_days']}") print(f" Total team hours: {month_data['team_totals']['total_hours']:.2f}h") print(f" Average active users per day: {month_data['team_totals']['average_daily_active']:.1f}") # Individual monthly performance print("\nIndividual Monthly Summary:") print("-" * 80) print(f"{'Name':<20} {'Total Hours':<12} {'Days':<8} {'Avg/Day':<10} {'Attendance':<12}") print("-" * 80) # Sort users by attendance rate then total hours sorted_users = sorted(month_data['users'].items(), key=lambda x: (x[1]['attendance_rate'], x[1]['total_hours']), reverse=True) for user_id, user_data in sorted_users: if user_data['total_hours'] > 0: name = user_data['name'][:19] total = f"{user_data['total_hours']:.2f}h" days = str(user_data['days_active']) avg = f"{user_data['average_hours_per_day']:.2f}h" attendance = f"{user_data['attendance_rate']:.1f}%" print(f"{name:<20} {total:<12} {days:<8} {avg:<10} {attendance:<12}") # Performance categories print("\nPerformance Analysis:") high_performers = [u for u in month_data['users'].values() if u['attendance_rate'] >= 90] regular_performers = [u for u in month_data['users'].values() if 70 <= u['attendance_rate'] < 90] low_attendance = [u for u in month_data['users'].values() if 0 < u['attendance_rate'] < 70] print(f" High attendance (≥90%): {len(high_performers)} users") print(f" Regular attendance (70-89%): {len(regular_performers)} users") print(f" Low attendance (<70%): {len(low_attendance)} users") print("=" * 80) def save_summary_to_file(summary_type, data, filename=None): """Save summary to a text file""" if not filename: timestamp = datetime.now(TIMEZONE).strftime('%Y%m%d_%H%M%S') filename = SUMMARIES_DIR / f"team_{summary_type}_{timestamp}.txt" try: with open(filename, 'w') as f: # Redirect print to file import contextlib with contextlib.redirect_stdout(f): if summary_type == 'daily': print_daily_summary(data) elif summary_type == 'weekly': print_weekly_summary(data) elif summary_type == 'monthly': print_monthly_summary(data) print(f"Summary saved to: {filename}") return filename except Exception as e: print(f"Failed to save summary: {e}") return None def main(): """Main function""" parser = argparse.ArgumentParser(description='Generate team summaries') parser.add_argument( '--type', choices=['daily', 'weekly', 'monthly', 'all'], default='daily', help='Type of summary to generate' ) parser.add_argument( '--date', type=str, help='Date for daily summary (YYYY-MM-DD), defaults to yesterday' ) parser.add_argument( '--week', type=str, help='Week start date for weekly summary (YYYY-MM-DD)' ) parser.add_argument( '--month', type=str, help='Month for monthly summary (YYYY-MM)' ) parser.add_argument( '--save', action='store_true', help='Save summary to file' ) parser.add_argument( '--csv', action='store_true', help='Save to CSV format' ) args = parser.parse_args() # Setup logging logger = setup_logging('team_summary') try: # Validate configuration validate_config() # Initialize team handler team_handler = get_team_handler() # Generate appropriate summary if args.type == 'daily' or args.type == 'all': # Parse date if provided if args.date: target_date = datetime.strptime(args.date, '%Y-%m-%d').date() else: target_date = (datetime.now(TIMEZONE) - timedelta(days=1)).date() logger.info(f"Generating daily summary for {target_date}") daily_summary = team_handler.generate_daily_team_summary(target_date) if daily_summary: print_daily_summary(daily_summary) if args.csv: team_handler.save_team_summary_to_csv(daily_summary) print(f"CSV saved to: {team_handler.team_summary_file}") if args.save: save_summary_to_file('daily', daily_summary) if args.type == 'weekly' or args.type == 'all': # Parse week start date if provided week_start = None if args.week: week_start = datetime.strptime(args.week, '%Y-%m-%d').date() logger.info(f"Generating weekly summary") weekly_summary = team_handler.generate_weekly_summary(week_start) if weekly_summary: print_weekly_summary(weekly_summary) if args.save: save_summary_to_file('weekly', weekly_summary) if args.type == 'monthly' or args.type == 'all': # Parse month if provided year = None month = None if args.month: year, month = map(int, args.month.split('-')) logger.info(f"Generating monthly summary") monthly_summary = team_handler.generate_monthly_summary(year, month) if monthly_summary: print_monthly_summary(monthly_summary) if args.save: save_summary_to_file('monthly', monthly_summary) return 0 except Exception as e: logger.error(f"Failed to generate summary: {e}", exc_info=True) print(f"ERROR: {e}", file=sys.stderr) return 1 if __name__ == "__main__": sys.exit(main())