Report Mailer
ivy.agent.report-mailer · v1.1.1
ivyx
Reads a sales sheet, sums the amounts per month, draws the chart and mails the monthly table with the chart attached, with no model involved.
Steps
In the order the agent's file lists them, each with where its inputs come from and the values the file fixes. A branch or a loop is a step too.
- 01Ivy NodeRead the sheetivy.node.excel-read
Reads one sheet of an Excel workbook into rows with the column names, row count and the workbook's sheet names.
- takes
pathfrom the agent's inputsheetfrom the agent's input
- 02Ivy NodeClean the numbers and datesivy.node.table-clean
Cleans rows for analysis: trims text, drops empty rows, turns number columns into numbers and date columns into ISO dates.
- takes
rowsfrom step 1- set
number_columns["amount"]date_columns["date"]
- 03Ivy NodeSum per monthivy.node.table-aggregate
Groups rows and sums, averages, counts or takes the minimum or maximum of one column, optionally by month, year or day of a date column.
- takes
rowsfrom step 2- set
group_by["date"]valueamountopsumperiodmonth- returns
groups
- 04Ivy NodeDraw the chartivy.node.chart-image
Draws a bar or line chart of two columns of rows and writes it as a PNG file.
- takes
rowsfrom step 3- set
pathreports/sales.pngxdateyamountkindbartitleMonthly saleswidth800height400- returns
chart
- 05Ivy NodeLay out the tableivy.node.markdown-table
Formats rows as a Markdown table, with the columns in the order given or as they first appear.
- takes
rowsfrom step 3- returns
table
- 06Ivy NodeWrite the bodyivy.node.template-render
Fills {{ name }} placeholders in a text template from a mapping, and reports which names were missing.
- takes
values.filefrom the agent's input,pathvalues.tablefrom step 5,markdown- set
templateMonthly sales from {{ file }}: {{ table }} The chart is attached.on_missingempty
- 07Ivy NodeAttach the chartivy.node.email-builder
Builds an email message with a plain-text body and an optional attachment, given as bytes or as a file path, and returns it with its attachment count.
- takes
from_emailfrom the agent's input,usernameto_emailfrom the agent's input,tobodyfrom step 6,textfile_pathfrom step 4,path- set
subjectSales report- returns
attachments
- 08Ivy NodeSend the reportivy.node.email-provider
Sends an email message through an SMTP server and returns the recipients it went to.
- takes
email_messagefrom step 7adminfrom the agent's input,usernamesmtp_serverfrom the agent's input,smtp_hostsmtp_portfrom the agent's inputuse_tlsfrom the agent's input,smtp_tls- set
smtp_serversmtp.gmail.comsmtp_port587use_tlstrue- secret
passwordthe stored secret mail-password- returns
sent,recipients
Nodes it brings
Adding this agent in IVYX Studio adds these nodes with it. A node your workspace already has is kept as it is, even at another version.
- Chart Imageivy.node.chart-image · v0.1.0
- Email Builderivy.node.email-builder · v0.2.0
- Email Providerivy.node.email-provider · v0.2.0
- Excel Readivy.node.excel-read · v0.1.0
- Markdown Tableivy.node.markdown-table · v0.1.0
- Table Aggregateivy.node.table-aggregate · v0.1.0
- Table Cleanivy.node.table-clean · v0.1.0
- Template Renderivy.node.template-render · v0.1.1
What it touches
Collected from what each of its nodes declares, plus the model call when a step is a model turn. A declaration is the author's statement, and it is what policy rules select on.
What it needs
- A stored secret named
mail-password. The agent's file carries the name, never the value.
Inputs
| Field | Type | Description |
|---|---|---|
| pathrequired | string | Path to the .xlsx file. |
| sheet | string | The sheet to read; the first one when left out. |
| usernamerequired | string | The mailbox address, which is the login name; its password is the stored secret mail-password. |
| torequired | string | Who receives the report. |
| smtp_host | string | The SMTP server; smtp.gmail.com when left out. |
| smtp_port | integer | The SMTP port; 587 when left out. |
| smtp_tls | boolean | Upgrade SMTP with STARTTLS; on when left out. |
Outputs
| Field | Type | Description |
|---|---|---|
| sent | boolean | True once the server accepted the report. |
| recipients | array | Who received it. |
| attachments | integer | How many files were attached. |
| groups | integer | How many months the sheet covers. |
| chart | string | The chart that was written and attached. |
| table | string | The monthly table that was mailed. |
Tests
2 of 2 test cases passed on Oct 7, 2026, in the publisher's own environment, before this version was published. The registry keeps that record; it does not run the cases again.
Requires: python:3.9, an SMTP server (Gmail unless smtp_host says otherwise) with the sending account's password stored as the secret mail-password, for the cases, GreenMail on 127.0.0.1:3025 (mkdata-mail.py)
- q3-workbook
The Q3 export: three months, the chart attached, the mail accepted.
- given
smtp_host127.0.0.1smtp_port3025smtp_tlsfalsepathdata/sales.xlsxsheetSalesusernameagent@ivyx.testtoagent@ivyx.test- expects
sentequals trueattachmentsequals 1groupsequals 3recipientsmatches @chartmatches sales\.png$tablematches 2026-07[\s\S]*2026-08[\s\S]*2026-09
- q4-workbook
Another workbook gives other months; its first sheet is a note, so the sheet is named.
- given
smtp_host127.0.0.1smtp_port3025smtp_tlsfalsepathdata/q4/q4-export.xlsxsheetSalesusernameagent@ivyx.testtoagent@ivyx.test- expects
sentequals trueattachmentsequals 1tablematches 2026-10[\s\S]*2026-12