Claude Code Plugins

Community-maintained marketplace

Feedback

Report templating with Jinja2 and MJML for SignalRoom. Use when creating new reports, modifying templates, debugging report rendering, or adding new notification channels.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name reports
description Report templating with Jinja2 and MJML for SignalRoom. Use when creating new reports, modifying templates, debugging report rendering, or adding new notification channels.

Report Templating System

Architecture

reports/
├── registry.py      # Report definitions (name, templates, query)
├── renderer.py      # Jinja2 + MJML rendering
├── runner.py        # Execute reports (query → render → send)
├── templates/       # .j2 (Slack/SMS) and .mjml (Email)
└── queries/         # SQL files for report data

Creating a New Report

1. Add SQL Query

Create src/signalroom/reports/queries/{report_name}.sql:

-- Parameters available: :date, :start_date, :end_date
SELECT
    :date AS report_date,
    SUM(conversions) AS total_conversions,
    SUM(revenue) AS total_revenue
FROM everflow.daily_stats
WHERE date = :date

2. Create Templates

Slack (templates/{report_name}.slack.j2):

*{{ title }}* — {{ report_date }}

:chart_with_upwards_trend: *Performance Summary*
• Conversions: {{ "{:,}".format(total_conversions) }}
• Revenue: ${{ "{:,.2f}".format(total_revenue) }}

Email (templates/{report_name}.email.mjml):

<mjml>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text>
          <h1>{{ title }}</h1>
          <p>Conversions: {{ total_conversions }}</p>
        </mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

SMS (templates/{report_name}.sms.j2):

{{ title }}: {{ total_conversions }} conv, ${{ total_revenue }} rev

3. Register Report

Add to src/signalroom/reports/registry.py:

REPORTS = {
    "my_report": Report(
        name="my_report",
        title="My Report Title",
        query_file="my_report.sql",
        templates={
            "slack": "my_report.slack.j2",
            "email": "my_report.email.mjml",
            "sms": "my_report.sms.j2",
        },
    ),
}

Running Reports

Local Testing (No Send)

python -c "
from signalroom.reports import run_report
print(run_report('daily_ccw', channel='slack'))
"

With Custom Parameters

python -c "
from signalroom.reports import run_report
print(run_report('daily_ccw', params={'date': '2025-12-18'}))
"

Send to Channel

python -c "
from signalroom.reports import run_report
run_report('daily_ccw', channel='slack', send=True)
"

Via Temporal

python scripts/trigger_workflow.py --report daily_ccw -w

Available Reports

Report Channels Description
daily_ccw slack, email, sms Daily CCW performance summary
test_sync slack Simple Everflow + Redtrack totals
alert slack, email, sms Error/warning/info alerts

Channel-Specific Formatting

Slack (mrkdwn)

*bold* _italic_ ~strikethrough~
:emoji_name:
• bullet point
```code```

Email (MJML)

MJML compiles to responsive HTML. Use components:

  • <mj-section> — row container
  • <mj-column> — column within section
  • <mj-text> — text content
  • <mj-button> — CTA button
  • <mj-image> — images

SMS

Keep under 160 characters. No formatting.

Jinja2 Patterns

Number Formatting

{{ "{:,}".format(value) }}           # 1,234,567
{{ "{:.2f}".format(value) }}         # 1234.56
{{ "${:,.2f}".format(value) }}       # $1,234.56
{{ "{:.1%}".format(value) }}         # 12.3%

Conditionals

{% if value > 0 %}
:arrow_up: Up {{ value }}%
{% else %}
:arrow_down: Down {{ value|abs }}%
{% endif %}

Loops

{% for row in affiliates %}
• {{ row.name }}: {{ row.conversions }} conv
{% endfor %}

Date Formatting

{{ report_date.strftime('%B %d, %Y') }}  # December 19, 2025
{{ report_date.strftime('%m/%d') }}       # 12/19

Alert Helper

For quick alerts without full reports:

from signalroom.reports import render_alert

message = render_alert(
    title="Pipeline Failed",
    message="Everflow sync failed with timeout",
    level="error"  # error, warning, info
)

Debugging

Template Not Found

Check path in registry matches actual file in templates/

SQL Error

Run query directly:

python -c "
from signalroom.reports.runner import execute_query
print(execute_query('daily_ccw', {'date': '2025-12-18'}))
"

MJML Compile Error

Test MJML syntax: https://mjml.io/try-it-live

Resources