#!/usr/bin/env python3 """ Generate and send weekly research digest. Compiles medium-priority findings from the week into a readable report. """ import sys import json import argparse from datetime import datetime, timedelta from pathlib import Path from collections import defaultdict sys.path.insert(0, str(Path(__file__).parent)) from config import load_config, get_topic, FINDINGS_DIR def get_week_range(offset_weeks: int = 0) -> tuple[datetime, datetime]: """Get start and end of current week (or offset).""" today = datetime.now() # Find most recent Sunday days_since_sunday = (today.weekday() + 1) % 7 sunday = today - timedelta(days=days_since_sunday) # Apply offset start = sunday - timedelta(weeks=offset_weeks) end = start + timedelta(days=6, hours=23, minutes=59, seconds=59) return start, end def load_week_findings(start: datetime, end: datetime) -> dict: """Load all findings from the week.""" if not FINDINGS_DIR.exists(): return {} findings_by_topic = defaultdict(list) # Scan findings directory for findings_file in FINDINGS_DIR.glob("*.json"): # Parse filename: YYYY-MM-DD_topic-id.json parts = findings_file.stem.split("_", 1) if len(parts) != 2: continue date_str, topic_id = parts try: file_date = datetime.strptime(date_str, "%Y-%m-%d") except ValueError: continue # Check if in week range if not (start <= file_date <= end): continue # Load findings with open(findings_file) as f: findings = json.load(f) findings_by_topic[topic_id].extend(findings) return dict(findings_by_topic) def generate_digest(findings_by_topic: dict, start: datetime, end: datetime) -> str: """Generate digest markdown.""" config = load_config() # Header digest = f"# šŸ“Š Weekly Research Digest\n\n" digest += f"**{start.strftime('%B %d')} - {end.strftime('%B %d, %Y')}**\n\n" digest += "---\n\n" # Summary stats total_findings = sum(len(f) for f in findings_by_topic.values()) topic_count = len(findings_by_topic) if total_findings == 0: digest += "No new findings this week.\n" return digest digest += f"šŸ“ˆ **Summary:** {total_findings} findings across {topic_count} topic(s)\n\n" digest += "---\n\n" # Highlights (highest scored findings) all_findings = [] for topic_id, findings in findings_by_topic.items(): topic = get_topic(topic_id) if not topic: continue for finding in findings: all_findings.append({ "topic": topic, "finding": finding }) # Sort by score all_findings.sort(key=lambda x: x["finding"].get("score", 0), reverse=True) if len(all_findings) >= 3: digest += "## šŸ”„ Top Highlights\n\n" for item in all_findings[:3]: topic = item["topic"] finding = item["finding"] result = finding.get("result", {}) score = finding.get("score", 0) digest += f"### {topic.get('name')} ({score:.2f})\n\n" digest += f"**{result.get('title', 'Untitled')}**\n\n" digest += f"{result.get('snippet', '')}\n\n" digest += f"šŸ”— [{result.get('url', '')}]({result.get('url', '')})\n\n" digest += "---\n\n" # Findings by topic digest += "## šŸ“š Findings by Topic\n\n" for topic_id, findings in sorted(findings_by_topic.items()): topic = get_topic(topic_id) if not topic: continue topic_name = topic.get("name", topic_id) topic_emoji = topic.get("emoji", "šŸ“Œ") digest += f"### {topic_emoji} {topic_name}\n\n" digest += f"**{len(findings)} finding(s) this week**\n\n" # Sort findings by score sorted_findings = sorted(findings, key=lambda x: x.get("score", 0), reverse=True) for finding in sorted_findings[:5]: # Top 5 per topic result = finding.get("result", {}) score = finding.get("score", 0) reason = finding.get("reason", "") sentiment = finding.get("sentiment", "") digest += f"- **{result.get('title', 'Untitled')}** ({score:.2f})\n" digest += f" {result.get('snippet', '')[:150]}...\n" digest += f" šŸ”— {result.get('url', '')}\n" if reason: digest += f" _Reason: {reason}_\n" if sentiment: digest += f" _Sentiment: {sentiment}_\n" digest += "\n" if len(sorted_findings) > 5: digest += f"_...and {len(sorted_findings) - 5} more_\n\n" digest += "\n" # Recommendations (future enhancement) digest += "---\n\n" digest += "## šŸ’” Recommendations\n\n" digest += "_Feature coming soon: AI-powered topic suggestions based on your findings_\n\n" return digest def send_digest(digest: str, dry_run: bool = False): """Send digest via configured channels.""" config = load_config() settings = config.get("settings", {}) # For now, just print (would integrate with message tool in real environment) if dry_run: print("\n" + "="*60) print("DIGEST PREVIEW:") print("="*60 + "\n") print(digest) print("\n" + "="*60) else: # Would send via Telegram, Discord, Email print("šŸ“§ Sending digest...") print(digest) print("\nāœ… Digest sent") def main(): parser = argparse.ArgumentParser(description="Generate weekly research digest") parser.add_argument("--preview", action="store_true", help="Preview without sending") parser.add_argument("--send", action="store_true", help="Generate and send") parser.add_argument("--week-offset", type=int, default=0, help="Week offset (0=current, 1=last week, etc.)") args = parser.parse_args() # Get week range start, end = get_week_range(args.week_offset) print(f"šŸ“Š Generating digest for {start.strftime('%Y-%m-%d')} to {end.strftime('%Y-%m-%d')}") # Load findings findings_by_topic = load_week_findings(start, end) if not findings_by_topic: print("āš ļø No findings for this period") return # Generate digest digest = generate_digest(findings_by_topic, start, end) # Send or preview if args.send: send_digest(digest, dry_run=False) else: send_digest(digest, dry_run=True) if __name__ == "__main__": main()