Skip to content

Cron Report Generation — Automated Business and Operational Reports

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Cron Report Generation. We cover key concepts, practical examples, and best practices to help you master this topic.

Learn cron-based report generation: schedule automated business reports that query databases, generate CSV/Excel exports, create PDF summaries with charts, distribute via email and Slack, and monitor report delivery success.

What You Learn

You will learn how to use cron for automated report generation: SQL query scheduling, data aggregation and transformation, report formatting and export, distribution via email and messaging, and delivery monitoring.

Why It Matters

Manual report generation is error-prone, inconsistent, and consumes developer time. Cron automation ensures reports are generated on schedule, with consistent formatting, and delivered to the right stakeholders without manual intervention.

Real-World Use

DodaTech's reporting Cron Jobs generate 15 reports daily: revenue summary at 7 AM, user growth at 8 AM, error rate report at 9 AM, and capacity report at 10 AM. Weekly reports are generated every Monday and sent to department heads via email with CSV attachments.

SQL Report Generator

import csv
import json
import io
from datetime import datetime, timedelta

class ReportGenerator:
    def __init__(self, report_name):
        self.report_name = report_name
        self.queries = []

    def add_query(self, name, query_fn, format='csv'):
        self.queries.append({'name': name, 'query': query_fn, 'format': format})

    def generate(self):
        print(f"Generating report: {self.report_name}")
        results = {}
        for q in self.queries:
            print(f"  Running query: {q['name']}...")
            data = q['query']()
            results[q['name']] = data

            if q['format'] == 'csv':
                output = io.StringIO()
                writer = csv.writer(output)
                writer.writerow(data.get('headers', []))
                for row in data.get('rows', []):
                    writer.writerow(row)
                print(f"    {len(data.get('rows', []))} rows generated")

        return results

def daily_revenue_query():
    return {
        'headers': ['date', 'revenue', 'orders', 'aov'],
        'rows': [
            ['2026-06-28', 12500.00, 312, 40.06],
            ['2026-06-27', 11800.00, 298, 39.60],
            ['2026-06-26', 13200.00, 334, 39.52],
            ['2026-06-25', 10900.00, 278, 39.21],
        ]
    }

report = ReportGenerator("Daily Revenue Summary")
report.add_query("revenue_by_day", daily_revenue_query, format='csv')
report.generate()

Expected output:

Generating report: Daily Revenue Summary
  Running query: revenue_by_day...
    4 rows generated

Report Distribution

import json
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

class ReportDistributor:
    def __init__(self):
        self.recipients = []

    def add_recipient(self, email, name, report_types):
        self.recipients.append({'email': email, 'name': name, 'reports': report_types})

    def distribute(self, report_name, content, format='csv'):
        for recipient in self.recipients:
            if report_name not in recipient['reports']:
                continue
            print(f"Sending {report_name} to {recipient['name']} <{recipient['email']}>")
            print(f"  Format: {format}")
            print(f"  Content size: {len(content)} bytes")

    def send_slack(self, webhook_url, report_name, summary):
        payload = {
            "channel": "#reports",
            "username": "ReportBot",
            "text": f"*{report_name}*\n{summary}",
        }
        print(f"Slack notification sent to #reports")
        return json.dumps(payload)

distributor = ReportDistributor()
distributor.add_recipient("ops@dodatech.com", "Ops Team", ["daily_revenue", "error_report"])
distributor.add_recipient("finance@dodatech.com", "Finance Team", ["daily_revenue"])

distributor.distribute("daily_revenue", "date,revenue,orders\n2026-06-28,12500,312", format='csv')
distributor.send_slack("https://hooks.slack.com/services/xxx", "Daily Revenue", "Revenue: $12,500 | Orders: 312 | AOV: $40.06")

Expected output:

Sending daily_revenue to Ops Team <ops@dodatech.com>
  Format: csv
  Content size: 47 bytes
Sending daily_revenue to Finance Team <finance@dodatech.com>
  Format: csv
  Content size: 47 bytes
Slack notification sent to #reports

Common Mistakes

1. Reports Generated But Never Read

A report that nobody reads wastes compute and storage. Survey stakeholders before creating reports. Send reports to distribution lists that opt in. Monitor email open rates and retire unused reports.

2. No Error Handling in Report Generation

A SQL query that fails mid-report generates a partial or corrupt report. Wrap each query in error handling. Generate partial reports with error annotations rather than failing entirely. Send error notifications to the report owner.

3. Reports During Peak Database Load

Running heavy aggregation queries during business hours slows the database for users. Schedule report generation during off-peak hours (e.g., 3-5 AM). Use read replicas for report queries.

4. No Report Versioning

If a report format changes mid-month, data becomes inconsistent. Version report formats and maintain backward compatibility. Archive past reports with their original format. Document report schema changes.

5. Large Attachments Exceeding Email Limits

A report with 100,000 rows (5+ MB) may exceed email attachment limits. Split large reports into multiple files, upload to cloud storage and include links, or use a reporting portal instead of email.

Practice Questions

1. How do you schedule report generation with cron?

Set cron expressions for each report's desired frequency: daily at 7 AM (0 7 * * *), weekly on Monday (0 7 * * 1), monthly on the 1st (0 7 1 * *). Each cron job runs the report script and distribution script.

2. What is the best format for automated reports?

CSV for data analysis (import into Excel/Google Sheets). PDF for executive summaries. HTML emails for quick consumption. JSON for machine-readable data. Choose the format based on the audience.

3. How do you handle report generation failures?

Implement retry logic (3 attempts with 5-minute backoff). Send failure notifications to the report owner. Include the error details and partial report if available. Log all failures for trend analysis.

4. How do you distribute reports to different audiences?

Use distribution lists per report type. Finance gets revenue reports, ops gets error reports, executives get summary reports. Support multiple delivery channels: email, Slack, S3, web portal.

Challenge

Build a report generation system: (1) report scheduler: daily revenue at 7 AM, weekly growth at 9 AM Monday, monthly executive summary at 8 AM 1st, (2) format support: CSV for data, HTML for email, PDF for executive, JSON for APIs, (3) query layer: SQL query execution with read replica routing, query timeout handling (300s max), partial result support, (4) distribution: email via SMTP, Slack Webhook, S3 upload, (5) monitoring: report generation duration, rows generated, delivery success rate, (6) delivery dashboard showing report status, history, and trends.

FAQ

How do I generate PDF reports with cron?

Use a headless browser (Puppeteer, Playwright) to render HTML to PDF. Write the report as HTML with charts (Chart.js), then convert to PDF. Or use Python libraries: WeasyPrint, ReportLab, or FPDF.

Should report generation use the primary or replica database?

Always use a read replica for report queries. Report queries are often heavy aggregation queries that can slow the primary database. Using a replica ensures report generation does not impact user-facing traffic.

How do I handle timezone differences in reports?

Store all data in UTC. Convert to the recipient's timezone at display time. Include the timezone in report headers. Let users configure their preferred timezone in the report distribution settings.

What is the best way to distribute reports to external stakeholders?

Use email for external distribution with the report as a PDF attachment. Upload to a secure portal for large reports. Use SFTP for machine-to-machine report delivery. Never send sensitive data over unencrypted channels.

How do I archive past reports?

Store reports in structured storage: one directory per report type, one subdirectory per date. Compress reports older than 30 days. Archive to cold storage after 1 year. Implement a retention policy per report type.

Mini Project: Automated Report System

Build a cron-based report generation and distribution system: (1) report definitions with SQL queries, format (CSV/HTML/PDF/JSON), schedule (cron expression), and distribution list, (2) report engine: execute queries on read replicas with 300s timeout, transform results into requested format, (3) distributor: email (SMTP with attachments), Slack (webhook with summary), S3 (JSON upload for APIs), (4) error handling: retry 3 times with 5-minute backoff, partial report on failure, error notification to owner, (5) monitoring: generation duration, row count, delivery success, format conversion time, (6) archive: 90 days in hot storage, then compress, retention per report type.

What's Next

Now that you understand report generation with cron, explore scheduled data scraping, then learn about email campaign scheduling.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro