Table-Based Email Automation
Outline
📥️ Pull Emails (IMAP)
A good practice is to read inputs that are coming from Envirnoment Variables. This will help passing down configurations from (Docker) Containers and/or Kubernetes to my actual running code.
Here are the Environment Variables needed:
USERNAME=email@gmail.com
APP_PASSWORD=app-password
IMAP_SERVER=imap.gmail.com
IMAP_PORT=993
IMAP_OUTPUT_DIR=./data #Optional; flags for inbox process
IMAP_FROM_EMAIL=email<+filter>@gmail.com
IMAP_TO_EMAIL=email<+filter>@gmail.com
######################
# OPTIONAL VARIABLES #
######################
IMAP_N_COUNT=1
IMAP_MAILBOX=INBOX
IMAP_CSV_COL_MESSAGE_ID=msgId
IMAP_CSV_COL_MESSAGE_REF=msgRef
IMAP_CSV_COL_DATE=date
IMAP_CSV_COL_FROM=email
IMAP_CSV_COL_SUBJECT=subject
IMAP_CSV_COL_ATTACHMENT_PATH=attachment
!source .env # load ENV variables from file; if needed
!pip install yoga1290.emailtable
from yoga1290.emailtable import processIMAPToCSV
processIMAPToCSV(
# USERNAME= os.getenv('USERNAME', 'USERNAME-REQUIRED@gmail.com'),
# APP_PASSWORD= os.getenv('APP_PASSWORD', 'APP-PASSWORD'),
# IMAP_SERVER= os.getenv('IMAP_SERVER', 'imap.gmail.com'),
# IMAP_PORT= int(os.getenv('IMAP_PORT', 993)),
# IMAP_MAILBOX= os.getenv('IMAP_MAILBOX', 'INBOX'),
# IMAP_N_COUNT= os.getenv('IMAP_N_COUNT', '5'),
# IMAP_FROM_EMAIL=os.getenv('IMAP_FROM_EMAIL', None), # example: '<username>+<filter>@gmail.com'
# IMAP_TO_EMAIL=os.getenv('IMAP_TO_EMAIL', None), # example: '<username>+<filter>@gmail.com'
# IMAP_OUTPUT_DIR=os.getenv('IMAP_OUTPUT_DIR', './data'),
# IMAP_CSV_COL_MESSAGE_ID=os.getenv('IMAP_CSV_COL_MESSAGE_ID', 'msgId'),
# IMAP_CSV_COL_MESSAGE_REF=os.getenv('IMAP_CSV_COL_MESSAGE_REF', 'msgRef'),
# IMAP_CSV_COL_DATE=os.getenv('IMAP_CSV_COL_DATE', 'date'),
# IMAP_CSV_COL_FROM=os.getenv('IMAP_CSV_COL_FROM', 'from'),
# IMAP_CSV_COL_SUBJECT=os.getenv('IMAP_CSV_COL_SUBJECT', 'subject'),
# IMAP_CSV_COL_ATTACHMENT_PATH=os.getenv('IMAP_CSV_COL_ATTACHMENT_PATH', 'attachment'),
)
import pandas as pd
import os
IMAP_OUTPUT_DIR=os.getenv("IMAP_OUTPUT_DIR", None)
outboxDF = pd.read_csv(f'{IMAP_OUTPUT_DIR}/output.csv')
outboxDF.head(5)
| msgId | msgRef | date | subject | content_path |
|---|
📝️ Draft emails
The Environment Variables passed as parameters:
USERNAME=email@gmail.com
APP_PASSWORD=app-password
IMAP_SERVER=imap.gmail.com
IMAP_PORT=993
DRAFT_CSV_PATH=./data/output.csv
DRAFT_CSV_EMAIL_COLUMN=email
DRAFT_CSV_MESSAGE_ID_COLUMN=msgId
DRAFT_CSV_REFERENCES_COLUMN=msgRef
DRAFT_CSV_SUBJECT_COLUMN=subject
DRAFT_CSV_FILE_COLUMN=content_path
DRAFTS_FOLDER="[Gmail]/Drafts"
!source .env # load ENV variables from file; if needed
!pip install yoga1290.emailtable
from yoga1290.emailtable import proccessCSVDrafts
proccessCSVDrafts(
# USERNAME=os.getenv("USERNAME", "USERNAME-REQUIRED@gmail.com"),
# APP_PASSWORD=os.getenv("APP_PASSWORD", "APP-PASSWORD"),
# IMAP_SERVER=os.getenv("IMAP_SERVER", "imap.gmail.com"),
# IMAP_PORT=os.getenv("IMAP_PORT", "993"),
# FROM_EMAIL=os.getenv("FROM_EMAIL", None),
# DRAFT_CSV_PATH=os.getenv("DRAFT_CSV_PATH", None),
# DRAFT_CSV_FILE_COLUMN=os.getenv("DRAFT_CSV_FILE_COLUMN", None),
# DRAFT_CSV_EMAIL_COLUMN=os.getenv("DRAFT_CSV_EMAIL_COLUMN", None),
# DRAFT_CSV_MESSAGE_ID_COLUMN=os.getenv(
# "DRAFT_CSV_MESSAGE_ID_COLUMN", None
# ),
# DRAFT_CSV_REFERENCES_COLUMN=os.getenv(
# "DRAFT_CSV_REFERENCES_COLUMN", None
# ),
# DRAFT_CSV_SUBJECT_COLUMN=os.getenv("DRAFT_CSV_SUBJECT_COLUMN", None),
# DRAFT_FOLDER=os.getenv("DRAFTS_FOLDER", "[Gmail]/Drafts"),
)
import pandas as pd
import os
DRAFT_CSV_PATH=os.getenv("DRAFT_CSV_PATH", None)
outboxDF = pd.read_csv(f'{DRAFT_CSV_PATH}')
outboxDF.head(5)
| msgId | msgRef | date | subject | content_path | Status |
|---|
📤️ Send Emails (SMTP)
!source .env # load ENV variables from file; if needed
!pip install yoga1290.emailtable
from yoga1290.emailtable import processSMTP
processSMTP(
# USERNAME= os.getenv('USERNAME', 'USERNAME-REQUIRED@gmail.com'),
# APP_PASSWORD= os.getenv('APP_PASSWORD', 'APP-PASSWORD'),
# SMTP_SERVER= os.getenv('SMTP_SERVER', 'smtp.gmail.com'),
# SMTP_PORT= int(os.getenv('SMTP_PORT', '587')),
# SMTP_CSV_PATH=os.getenv('SMTP_CSV_PATH', None),
# SMTP_CSV_FILE_COLUMN=os.getenv('SMTP_CSV_FILE_COLUMN', None),
# SMTP_CSV_EMAIL_COLUMN=os.getenv('SMTP_CSV_EMAIL_COLUMN', None),
# SMTP_CSV_SUBJECT_COLUMN=os.getenv('SMTP_CSV_SUBJECT_COLUMN', None),
)
import pandas as pd
import os
SMTP_CSV_PATH=os.getenv('SMTP_CSV_PATH', None)
pd.read_csv(SMTP_CSV_PATH).head(5)
| msgId | msgRef | date | subject | content_path |
|---|
📦️ Run in Docker Containers
One way I use it, I trigger it as part of chain of jobs, each runs on seperate docker container; running one by one, example: emailtable-job2 following emailtable-job1, so my docker-compose.yml would look like the following:
# docker-compose.yml
emailtable-job2:
env_file:
- .env
depends_on:
emailtable-job1:
condition: service_completed_successfully
image: ghcr.io/yoga1290/python-emailtable:26.10
# my docker image here is just based on python:3.13-alpine with my package pre-installed; saves network over time.
working_dir: /app
volumes:
- .:/app
command:
- python
- -c
- |
from yoga1290.emailtable import (
proccessCSVDrafts,
processIMAPToCSV,
processSMTP )
# proccessCSVDrafts() #...
$ > docker compose up emailtable-job2
Metadata
Release files for yoga1290.emailtable 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| yoga1290_emailtable-0.1.1.tar.gz | 10.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| yoga1290_emailtable-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 20.4 kB
Release files / yoga1290_emailtable-0.1.1.tar.gz
| Download URL | yoga1290_emailtable-0.1.1.tar.gz |
|---|---|
| Size | 10.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
caf601f9bc062b6d2478ac3f3107472583391595d3ac82da2c6e46657c99870b
|
|
BLAKE2b-256 checksum How to use checksums |
54a53ab62a58d205b338f61f082057e44008ba0a61ad944a6276e6986b7710ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logRelease files / yoga1290_emailtable-0.1.1-py3-none-any.whl
| Download URL | yoga1290_emailtable-0.1.1-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
be353cd55885a33aa590f478766943af23bc3305f517f179e39941a3522d6bdf
|
|
BLAKE2b-256 checksum How to use checksums |
4e59568f161dab08de47ad921eb907b777ed6f81e09bd1197d932ade06a5b370
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logSecurity for yoga1290.emailtable 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for yoga1290_emailtable-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.