Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
03b5be8
Update README.md
juleswg23 Oct 8, 2025
c81ea95
conditional example skeleton
juleswg23 Oct 9, 2025
ee063f4
make whole game a tad prettier
juleswg23 Oct 9, 2025
e6db181
conditional recepients
juleswg23 Oct 9, 2025
41a4ab2
sending quarto output as email
juleswg23 Oct 9, 2025
db8cf3d
not sure all of this is working
juleswg23 Oct 10, 2025
0474c95
playing with data types and email previewing
juleswg23 Oct 10, 2025
44b9c2b
outline mjml_to_intermediate_struct
juleswg23 Oct 14, 2025
5b574ad
note on output files
juleswg23 Oct 14, 2025
ec754a9
Merge branch 'main' into sending-quarto-emails
juleswg23 Oct 14, 2025
14edb39
using dotenv
juleswg23 Oct 14, 2025
880f345
remove playground example
juleswg23 Oct 14, 2025
b171bfd
rename "package"
juleswg23 Oct 14, 2025
2c70279
add gitignore
juleswg23 Oct 14, 2025
56c63a8
deleting sys.path call
juleswg23 Oct 14, 2025
df48a0b
modify gitignore
juleswg23 Oct 14, 2025
c63aa51
upload mjml email to assets
juleswg23 Oct 14, 2025
3fae4c8
add __init__ for emailer package
juleswg23 Oct 14, 2025
df58868
refactor redmail to struct
juleswg23 Oct 15, 2025
3c7f131
rename class
juleswg23 Oct 15, 2025
c22b00d
rename class
juleswg23 Oct 15, 2025
fd5b313
package and docs setup
juleswg23 Oct 15, 2025
853584a
package and docs setup
juleswg23 Oct 15, 2025
c921526
re-factor
juleswg23 Oct 15, 2025
cdff2e1
initial add to quartodoc
juleswg23 Oct 15, 2025
3a53480
expose read_quarto_email_json
juleswg23 Oct 15, 2025
38a3301
add readme
juleswg23 Oct 15, 2025
6309c69
rename to quarto_json_to_intermediate_email, and update quarto yml
juleswg23 Oct 15, 2025
090f6b1
add tests scaffolding
juleswg23 Oct 15, 2025
ffc2dda
restructure package dirs
juleswg23 Oct 16, 2025
fd6bfd4
layout of docs, and writing some docstrings
juleswg23 Oct 16, 2025
0a1ec22
docs progress
juleswg23 Oct 16, 2025
7c7996b
vocab tweak
juleswg23 Oct 16, 2025
aa28d9f
remove none params
juleswg23 Oct 16, 2025
22b4da1
correction (thank you David)
juleswg23 Oct 16, 2025
1192eab
modify docs
juleswg23 Oct 16, 2025
3846951
separate methods of email object
juleswg23 Oct 16, 2025
874e634
insert subject in html emails
juleswg23 Oct 16, 2025
4dc2093
struct tests
juleswg23 Oct 16, 2025
f1d4336
test suite for egress functions
juleswg23 Oct 17, 2025
e675f7f
test util functions
juleswg23 Oct 17, 2025
4f63bd8
new test ci setup attempt
juleswg23 Oct 17, 2025
b975178
change deps
juleswg23 Oct 17, 2025
34534a1
trying new test ci
juleswg23 Oct 17, 2025
1ae6136
remove rowking directory from ci yml
juleswg23 Oct 17, 2025
b8ede18
Install emailer-lib
juleswg23 Oct 17, 2025
3d117de
Install emailer-lib
juleswg23 Oct 17, 2025
0c79605
intentional failing test
juleswg23 Oct 17, 2025
5e91361
test ingress
juleswg23 Oct 17, 2025
296ed92
test not implementeds
juleswg23 Oct 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,40 @@ jobs:
if: ${{ github.event_name == 'pull_request' }}
with:
source-dir: _site

test-emailer-lib:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.12"]
fail-fast: false
permissions:
pull-requests: write
actions: write
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: "Set up Python"
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install the project
run: uv sync --all-extras --dev

- name: Install emailer-lib
run: uv pip install -e "./emailer-lib"

- name: Test emailer-lib
run: |
uv run pytest emailer-lib/emailer_lib/tests/ --cov=emailer_lib --cov-report=xml --cov-report=term-missing

- name: Upload coverage reports
uses: codecov/codecov-action@v4
if: ${{ matrix.python-version == '3.12' }}
with:
file: emailer-lib/coverage.xml
flags: emailer-lib
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ __marimo__/

/.quarto/

_book
_site

.DS_Store
.DS_Store

.vscode
1 change: 1 addition & 0 deletions .output_metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"rsc_email_attachments":[],"rsc_email_body_html":"<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\"> <!-- utf-8 works for most cases -->\n<meta name=\"viewport\" content=\"width=device-width\"> <!-- Forcing initial-scale shouldn't be necessary -->\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"> <!-- Use the latest (edge) version of IE rendering engine -->\n<meta name=\"x-apple-disable-message-reformatting\"> <!-- Disable auto-scale in iOS 10 Mail entirely -->\n<meta name=\"format-detection\" content=\"telephone=no,address=no,email=no,date=no,url=no\"> <!-- Tell iOS not to automatically link certain text strings. -->\n<meta name=\"color-scheme\" content=\"light\">\n<meta name=\"supported-color-schemes\" content=\"light\">\n<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->\n<!--[if gte mso 9]>\n<xml>\n<o:OfficeDocumentSettings>\n<o:AllowPNG/>\n<o:PixelsPerInch>96</o:PixelsPerInch>\n</o:OfficeDocumentSettings>\n</xml>\n<![endif]-->\n<style>\nbody {\nfont-family: Helvetica, sans-serif;\nfont-size: 14px;\n}\n.content {\nbackground-color: white;\n}\n.content .message-block {\nmargin-bottom: 24px;\n}\n.header .message-block, .footer message-block {\nmargin-bottom: 12px;\n}\nimg {\nmax-width: 100%;\n}\n@media only screen and (max-width: 767px) {\n.container {\nwidth: 100%;\n}\n.articles, .articles tr, .articles td {\ndisplay: block;\nwidth: 100%;\n}\n.article {\nmargin-bottom: 24px;\n}\n}\n</style>\n</head>\n<body style=\"background-color:#f6f6f6;font-family:Helvetica, sans-serif;color:#222;margin:0;padding:0;\">\n<table width=\"85%\" align=\"center\" class=\"container\" style=\"max-width:1000px;\">\n<tr>\n<td style=\"padding:24px;\">\n<div class=\"header\" style=\"font-family:Helvetica, sans-serif;color:#999999;font-size:12px;font-weight:normal;margin:0 0 24px 0;text-align:center;\">\n</div>\n<table width=\"100%\" class=\"content\" style=\"background-color:white;\">\n<tr>\n<td style=\"padding:12px;\">\n<p>Below is a table including the last 10 days of stock market data from\n2015.</p>\n<div id=\"adbe1107\" class=\"cell\" data-execution_count=\"1\">\n<div class=\"quarto-scaffold\">\n<details class=\"code-fold\">\n<summary>Code</summary>\n<div class=\"sourceCode\" id=\"cb1\"><pre\nclass=\"sourceCode python cell-code\"><code class=\"sourceCode python\"><span id=\"cb1-1\"><a href=\"#cb1-1\" aria-hidden=\"true\" tabindex=\"-1\"></a><span class=\"im\">from</span> great_tables.data <span class=\"im\">import</span> sp500</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\" aria-hidden=\"true\" tabindex=\"-1\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\" aria-hidden=\"true\" tabindex=\"-1\"></a>sp500.head(<span class=\"dv\">10</span>).style</span></code></pre></div>\n</details>\n</div>\n</div>\n<p><strong>Thanks for reading!</strong></p>\n</td></tr>\n</table>\n<div class=\"footer\" style=\"font-family:Helvetica, sans-serif;color:#999999;font-size:12px;font-weight:normal;margin:24px 0 0 0;\">\n<p>This message was generated on 2025-10-16 11:37:43.</p>\n\n<p>If HTML documents are attached, they may not render correctly when viewed in some email clients. For a better experience, download HTML documents to disk before opening in a web browser.</p>\n</div>\n</td>\n</tr>\n</table>\n</body>\n</html>\n","rsc_email_subject":"Stock Market Report","rsc_email_suppress_report_attachment":true,"rsc_email_suppress_scheduled":false}
52 changes: 52 additions & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,63 @@ website:
- orchestrating-auth.qmd
- orchestrating-tests.qmd

quartodoc:
package: emailer_lib
dir: reference
title: API Reference
css: reference/_styles-quartodoc.css
renderer:
style: markdown
table_style: description-list

sections:
- title: The Email Object
desc: >
An email object that in a serializable, previewable format,
optimized for emails with content generated by data scientists.
contents:
- name: IntermediateEmail
children: separate
- name: IntermediateEmail.write_preview_email
- name: IntermediateEmail.write_email_message
- name: IntermediateEmail.preview_send_email
- title: Uploading emails
desc: >
Converting emails to IntermediateEmails,
at which point they can be previewed, tested, and sent.
contents:
- quarto_json_to_intermediate_email
- mjml_to_intermediate_email
- redmail_to_intermediate_email
- yagmail_to_intermediate_email

- title: Sending
desc: >
Functions to sending emails with different providers.
And a special handy one to bypass the intermediate object if you are sending a quarto email.
contents:
- send_intermediate_email_with_gmail
- send_intermediate_email_with_smtp
- send_intermediate_email_with_redmail
- send_intermediate_email_with_yagmail
- send_intermediate_email_with_mailgun
- send_quarto_email_with_gmail


- title: Utilities
desc: >
Previews and more
contents:
- write_email_message_to_file


format:
html:
theme:
- cosmo
- brand
css:
- reference/_styles-quartodoc.css
- reference/styles.css


Binary file added assets/mjml-email-full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
136 changes: 0 additions & 136 deletions email-examples/summary-example.qmd

This file was deleted.

2 changes: 2 additions & 0 deletions emailer-lib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# generated emails
*.html
5 changes: 5 additions & 0 deletions emailer-lib/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
test:
pytest --cov-report=xml

test-update:
pytest --snapshot-update
84 changes: 84 additions & 0 deletions emailer-lib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# emailer-lib

<!-- [![Python Versions](https://img.shields.io/pypi/pyversions/gt_extras.svg)](https://pypi.python.org/pypi/gt-extras) -->
<!-- [![PyPI](https://img.shields.io/pypi/v/gt-extras?logo=python&logoColor=white&color=orange)](https://pypi.org/project/gt-extras/) -->
<!-- [![PyPI Downloads](https://static.pepy.tech/badge/gt-extras)](https://pepy.tech/projects/gt-extras) -->
<!-- [![License](https://img.shields.io/github/license/posit-dev/email-for-data-science)](https://github.com/posit-dev/email-for-data-science/blob/main/LICENSE) -->

<!-- [![Tests](https://github.com/posit-dev/gt-extras/actions/workflows/ci_tests.yml/badge.svg)](https://github.com/posit-dev/gt-extras/actions/workflows/ci_tests.yml) -->
[![Documentation](https://img.shields.io/badge/docs-project_website-blue.svg)](https://posit-dev.github.io/email-for-data-science/reference/)
<!-- [![Repo Status](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip) -->
<!-- [![Contributors](https://img.shields.io/github/contributors/posit-dev/gt-extras)](https://github.com/posit-dev/gt-extras/graphs/contributors) -->
<!-- [![Codecov](https://codecov.io/gh/posit-dev/gt-extras/branch/main/graph/badge.svg)](https://codecov.io/gh/posit-dev/gt-extras) -->

> ⚠️ **emailer-lib is currently in development, expect breaking changes.**

### What is [emailer-lib](https://posit-dev.github.io/email-for-data-science/reference/)?

**emailer-lib** is a Python package for serializing, previewing, and sending email messages in a consistent, simple structure. It provides utilities to convert emails from different sources (Redmail, Yagmail, MJML, Quarto JSON) into a unified intermediate format, and send them via multiple backends (Gmail, SMTP, Mailgun, etc.).

The package is designed for data science workflows and Quarto projects, making it easy to generate, preview, and deliver rich email content programmatically.

<!-- ## Installation
Install the latest release from your local repo or PyPI:
```bash
pip install -e ./emailer-lib
```
-->

## Example Usage

```python
from emailer_lib import (
quarto_json_to_intermediate_email,
IntermediateEmail,
send_intermediate_email_with_gmail,
)

# Read a Quarto email JSON file
email_struct = quarto_json_to_intermediate_email("email.json")

# Preview the email as HTML
email_struct.write_preview_email("preview.html")

# Send the email via Gmail
send_intermediate_email_with_gmail("your_email@gmail.com", "your_password", email_struct)
```

## Features

- **Unified email structure** for serialization and conversion
- **Convert** emails from Redmail, Yagmail, MJML, and Quarto JSON
- **Send** emails via Gmail, SMTP, Mailgun, and more
- **Preview** emails as HTML files
- **Support for attachments** (inline and external)
- **Simple API** for integration in data science and reporting workflows

## Contributing
If you encounter a bug, have usage questions, or want to share ideas to make this package better, please feel free to file an [issue](https://github.com/posit-dev/email-for-data-science/issues).

<!--
## Code of Conduct
Please note that the **gt-extras** project is released with a [contributor code of conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).<br>By participating in this project you agree to abide by its terms. -->

<!-- ## 📄 License
**Great Tables** is licensed under the MIT license.
© Posit Software, PBC. -->

<!-- ## Citation
If you use **gt-extras** in your work, please cite the package:
```bibtex
@software{gt_extras,
authors = {Jules Walzer-Goldfeld, Michael Chow, and Rich Iannone},
license = {MIT},
title = {{gt-extras: Extra helpers for great-tables in Python.}},
url = {https://github.com/posit-dev/gt-extras}, version = {0.0.1}
}
``` -->

For more information, see the [docs](https://posit-dev.github.io/email-for-data-science/reference) or [open an issue](https://github.com/posit-dev/email-for-data-science/issues) with questions or suggestions!
35 changes: 35 additions & 0 deletions emailer-lib/emailer_lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from .ingress import (
redmail_to_intermediate_email,
yagmail_to_intermediate_email,
mjml_to_intermediate_email,
quarto_json_to_intermediate_email,
)

from .egress import (
send_quarto_email_with_gmail,
send_intermediate_email_with_gmail,
send_intermediate_email_with_redmail,
send_intermediate_email_with_yagmail,
send_intermediate_email_with_mailgun,
send_intermediate_email_with_smtp,
)

from .utils import write_email_message_to_file

from .structs import IntermediateEmail


__all__ = [
"quarto_json_to_intermediate_email",
"IntermediateEmail",
"redmail_to_intermediate_email",
"yagmail_to_intermediate_email",
"mjml_to_intermediate_email",
"send_quarto_email_with_gmail",
"send_intermediate_email_with_gmail",
"send_intermediate_email_with_redmail",
"send_intermediate_email_with_yagmail",
"send_intermediate_email_with_mailgun",
"send_intermediate_email_with_smtp",
"write_email_message_to_file",
]
Loading