mail-cadence 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mail_cadence-0.1.1/.gitignore +110 -0
- mail_cadence-0.1.1/AGENTS.md +36 -0
- mail_cadence-0.1.1/LICENSE +21 -0
- mail_cadence-0.1.1/PKG-INFO +242 -0
- mail_cadence-0.1.1/README.md +176 -0
- mail_cadence-0.1.1/pyproject.toml +162 -0
- mail_cadence-0.1.1/src/mail_cadence/__init__.py +8 -0
- mail_cadence-0.1.1/src/mail_cadence/cli.py +410 -0
- mail_cadence-0.1.1/src/mail_cadence/database.py +111 -0
- mail_cadence-0.1.1/src/mail_cadence/mailer.py +348 -0
- mail_cadence-0.1.1/src/mail_cadence/models.py +198 -0
- mail_cadence-0.1.1/src/mail_cadence/py.typed +0 -0
- mail_cadence-0.1.1/src/mail_cadence/reporter.py +132 -0
- mail_cadence-0.1.1/src/mail_cadence/repository.py +629 -0
- mail_cadence-0.1.1/src/mail_cadence/scheduler.py +140 -0
- mail_cadence-0.1.1/src/mail_cadence/templates/daily_dispatch.html.j2 +5 -0
- mail_cadence-0.1.1/src/mail_cadence/templates/report_email.html.j2 +79 -0
- mail_cadence-0.1.1/src/mail_cadence/templates/report_email.txt.j2 +26 -0
- mail_cadence-0.1.1/src/mail_cadence/templates/settlement_summary.html.j2 +6 -0
- mail_cadence-0.1.1/src/mail_cadence/web.py +395 -0
- mail_cadence-0.1.1/src/mail_cadence/web_templates/dashboard.html +79 -0
- mail_cadence-0.1.1/src/mail_cadence/web_templates/layout.html +42 -0
- mail_cadence-0.1.1/src/mail_cadence/web_templates/schedules.html +90 -0
- mail_cadence-0.1.1/src/mail_cadence/web_templates/settings.html +38 -0
- mail_cadence-0.1.1/src/mail_cadence/web_templates/tasks.html +141 -0
- mail_cadence-0.1.1/src/mail_cadence/xml_loader.py +117 -0
- mail_cadence-0.1.1/tests/test_cli.py +253 -0
- mail_cadence-0.1.1/tests/test_coverage_boost.py +165 -0
- mail_cadence-0.1.1/tests/test_database.py +253 -0
- mail_cadence-0.1.1/tests/test_e2e_integration.py +87 -0
- mail_cadence-0.1.1/tests/test_mailer.py +552 -0
- mail_cadence-0.1.1/tests/test_models.py +168 -0
- mail_cadence-0.1.1/tests/test_reporter.py +160 -0
- mail_cadence-0.1.1/tests/test_roundtrip.py +109 -0
- mail_cadence-0.1.1/tests/test_scheduler.py +428 -0
- mail_cadence-0.1.1/tests/test_smtp_e2e_integration.py +328 -0
- mail_cadence-0.1.1/tests/test_web.py +277 -0
- mail_cadence-0.1.1/tests/test_xml_loader.py +172 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# 1. Python Runtime & Build Artifacts
|
|
3
|
+
# ==============================================================================
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*$py.class
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
ENV/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Packaging and distribution artifacts (Hatchling / Wheel / PIP)
|
|
16
|
+
build/
|
|
17
|
+
dist/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.eggs/
|
|
21
|
+
*.egg
|
|
22
|
+
pip-wheel-metadata/
|
|
23
|
+
|
|
24
|
+
# ==============================================================================
|
|
25
|
+
# 2. Environment Configuration & Secrets (pydantic-settings)
|
|
26
|
+
# ==============================================================================
|
|
27
|
+
# Protect SMTP credentials (SMTP_USER, SMTP_PASSWORD) from leaking
|
|
28
|
+
.env
|
|
29
|
+
.env.*
|
|
30
|
+
!.env.example
|
|
31
|
+
!.env.template
|
|
32
|
+
|
|
33
|
+
# ==============================================================================
|
|
34
|
+
# 3. SQLite Databases (including PRAGMA journal_mode = WAL files)
|
|
35
|
+
# ==============================================================================
|
|
36
|
+
# Default project database (cli.py) and general SQLite files
|
|
37
|
+
mail_cadence.db
|
|
38
|
+
mail_cadence.db-shm
|
|
39
|
+
mail_cadence.db-wal
|
|
40
|
+
mail_cadence.db-journal
|
|
41
|
+
|
|
42
|
+
*.db
|
|
43
|
+
*.db-shm
|
|
44
|
+
*.db-wal
|
|
45
|
+
*.db-journal
|
|
46
|
+
*.sqlite
|
|
47
|
+
*.sqlite3
|
|
48
|
+
*.sqlite3-shm
|
|
49
|
+
*.sqlite3-wal
|
|
50
|
+
|
|
51
|
+
# ==============================================================================
|
|
52
|
+
# 4. Quality Gates, Testing & Static Analysis Caches
|
|
53
|
+
# ==============================================================================
|
|
54
|
+
# Pytest & Coverage (pytest-cov)
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
.coverage
|
|
57
|
+
.coverage.*
|
|
58
|
+
htmlcov/
|
|
59
|
+
coverage.xml
|
|
60
|
+
*.cover
|
|
61
|
+
.hypothesis/
|
|
62
|
+
|
|
63
|
+
# Mypy & Ruff
|
|
64
|
+
.mypy_cache/
|
|
65
|
+
.dmypy.json
|
|
66
|
+
dmypy.json
|
|
67
|
+
.ruff_cache/
|
|
68
|
+
|
|
69
|
+
# Other QA tools
|
|
70
|
+
.tox/
|
|
71
|
+
.nox/
|
|
72
|
+
.cache/
|
|
73
|
+
|
|
74
|
+
# ==============================================================================
|
|
75
|
+
# 5. Logs & Local Working Files (XML import/export)
|
|
76
|
+
# ==============================================================================
|
|
77
|
+
*.log
|
|
78
|
+
logs/
|
|
79
|
+
tmp/
|
|
80
|
+
temp/
|
|
81
|
+
|
|
82
|
+
# Local XML dump files (when generated outside tests/ or examples/)
|
|
83
|
+
*_export.xml
|
|
84
|
+
local_*.xml
|
|
85
|
+
|
|
86
|
+
# ==============================================================================
|
|
87
|
+
# 6. IDEs, Editors & OS-specific Files
|
|
88
|
+
# ==============================================================================
|
|
89
|
+
# JetBrains (PyCharm)
|
|
90
|
+
.idea/
|
|
91
|
+
*.iws
|
|
92
|
+
*.iml
|
|
93
|
+
*.ipr
|
|
94
|
+
|
|
95
|
+
# Zed / VS Code
|
|
96
|
+
.zed/
|
|
97
|
+
.vscode/
|
|
98
|
+
|
|
99
|
+
# Editor temporary files
|
|
100
|
+
*~
|
|
101
|
+
*.swp
|
|
102
|
+
*.swo
|
|
103
|
+
|
|
104
|
+
# Operating Systems (Linux / Windows / macOS)
|
|
105
|
+
.DS_Store
|
|
106
|
+
.DS_Store?
|
|
107
|
+
._*
|
|
108
|
+
Thumbs.db
|
|
109
|
+
ehthumbs.db
|
|
110
|
+
Desktop.ini
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Architectural and Quality Guidelines (AGENTS.md)
|
|
2
|
+
|
|
3
|
+
This document defines absolute engineering rules for the `mail-cadence` project. Any code changes must comply with the following principles before task completion.
|
|
4
|
+
|
|
5
|
+
## 1. Mandatory Quality Gates
|
|
6
|
+
Before declaring code readiness, run all of the following tools. None of them may return errors or warnings:
|
|
7
|
+
1. `pytest` – all unit and integration tests must pass.
|
|
8
|
+
2. `mypy src tests` – full compliance with `strict = true` mode.
|
|
9
|
+
3. `ruff check src tests` and `ruff format --check src tests` – zero linter rule violations (including the `C901 <= 10` complexity limit).
|
|
10
|
+
4. `vulture src` – zero dead code, unused private methods, and local variables.
|
|
11
|
+
5. `radon cc src -a -nb` – no function or method may have a complexity rating worse than `B`.
|
|
12
|
+
6. `pip-audit` – no known vulnerabilities in dependencies.
|
|
13
|
+
|
|
14
|
+
## 2. SQLite Database Layer (`database.py`, `repository.py`, `xml_loader.py`)
|
|
15
|
+
* **Ban on nested `with self.conn:` blocks:** In the `sqlite3` library, exiting a `with conn:` block immediately performs a `COMMIT` across the entire connection.
|
|
16
|
+
- Public methods in `DatabaseRepository` (e.g., `save_report_settings`, `save_work_schedules`, `save_mail_configuration`) open a `with self.conn:` block and delegate SQL queries to private methods (`_save_report_settings`, `_save_work_schedules`, `_save_mail_configuration`, `_save_task`) which **do not use** `with self.conn:`.
|
|
17
|
+
- Complex cross-module operations (e.g., `import_from_xml` in `xml_loader.py`) must open a single `with conn:` block at the highest level and call internal `_save_*` methods, guaranteeing a full `ROLLBACK` on error.
|
|
18
|
+
* **Foreign Key Constraints (`ON DELETE RESTRICT`):** The `tasks` table has a foreign key `schedule_ref` with the `ON DELETE RESTRICT` clause. In `mode="replace"`, always delete rows from `task_context_variables`, `task_recipients`, and `tasks` **before** deleting rows from `schedules`.
|
|
19
|
+
* **Ban on N+1 queries and execution loops:**
|
|
20
|
+
- When reading collections (e.g., `get_mail_tasks`, `get_work_schedules`), fetch related records via a bulk `SELECT` query and group them in memory using `collections.defaultdict`. Do not execute `SELECT` queries inside `for` loops.
|
|
21
|
+
- When writing collections (recipients, context variables, hour windows), always use `self.conn.executemany(...)` instead of `self.conn.execute(...)` in a loop.
|
|
22
|
+
|
|
23
|
+
## 3. SMTP Engine and Concurrency (`mailer.py`, `scheduler.py`, `cli.py`)
|
|
24
|
+
* **DRY Principle in `AsyncMailer`:** Management of the SMTP connection lifecycle (`_custom_smtp_client`, connection pool `self._pool`, single-use connection) must be implemented in a single place (e.g., via `@contextlib.asynccontextmanager`), without duplicating `try...except` blocks and `send_message` calls.
|
|
25
|
+
* **SMTP Error Classification and Retry:**
|
|
26
|
+
- Error codes in the range `500 <= code < 600` are permanent errors and **are not subject** to retries.
|
|
27
|
+
- Upon partial delivery (when some recipients receive the message while others return an error in the `errors` dictionary), filter out recipients with a permanent `5xx` error immediately and retry sending (`pending_recipients`) exclusively for recipients with transient errors (`4xx`).
|
|
28
|
+
* **Time and Clocks:**
|
|
29
|
+
- For measuring task execution duration (`duration_ms`) and in `TokenBucketRateLimiter`, use exclusively a monotonic clock (`time.monotonic()`).
|
|
30
|
+
- Execution timestamps (`last_run`, `start_time`, `end_time`) must always be stored as timezone-aware `datetime` objects (`datetime.now(UTC)` or `zoneinfo.ZoneInfo`).
|
|
31
|
+
|
|
32
|
+
## 4. Security (`models.py`, `xml_loader.py`, `mailer.py`)
|
|
33
|
+
* **XML Parsing:** Every XML file before being passed to `pydantic-xml` must be validated via `defusedxml.ElementTree` to protect against XXE and entity expansion attacks.
|
|
34
|
+
* **SMTP Header Injection Protection:** All fields going into message headers (`sender`, `recipient`, `subject`, `to`, `cc`, `bcc`) must be validated for the absence of `\r` and `\n` characters (`_check_no_newlines`).
|
|
35
|
+
* **Jinja2 Templates:** `TemplateRenderer` must validate paths using the `resolved_path.relative_to(base_dir)` method (Path Traversal protection) and keep `select_autoescape(["html", "xml"])` enabled.
|
|
36
|
+
* **Secrets and Credentials:** Never store real passwords or `.env` files in the repository. In tests, use exclusively local mock/sink servers on `127.0.0.1` and dummy domains (`@example.com`, `@localhost`).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bulk Mailer Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mail-cadence
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Asynchronous SQLite-backed bulk email dispatch and reporting engine with XML import/export support
|
|
5
|
+
Project-URL: Homepage, https://github.com/krj29b/mail-cadence
|
|
6
|
+
Project-URL: Repository, https://github.com/krj29b/mail-cadence
|
|
7
|
+
Project-URL: Issues, https://github.com/krj29b/mail-cadence/issues
|
|
8
|
+
Author: krj29b
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Bulk Mailer Contributors
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: aiosmtplib,email,fastapi,htmx,rate-limiter,scheduler,smtp,sqlite,xml
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Environment :: Web Environment
|
|
35
|
+
Classifier: Framework :: AsyncIO
|
|
36
|
+
Classifier: Framework :: FastAPI
|
|
37
|
+
Classifier: Framework :: Pydantic :: 2
|
|
38
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Communications :: Email
|
|
41
|
+
Classifier: Typing :: Typed
|
|
42
|
+
Requires-Python: >=3.12
|
|
43
|
+
Requires-Dist: aiosmtplib>=3.0.1
|
|
44
|
+
Requires-Dist: defusedxml>=0.7.1
|
|
45
|
+
Requires-Dist: fastapi>=0.115.0
|
|
46
|
+
Requires-Dist: jinja2>=3.1.4
|
|
47
|
+
Requires-Dist: lxml>=5.2.2
|
|
48
|
+
Requires-Dist: pydantic-settings>=2.4.0
|
|
49
|
+
Requires-Dist: pydantic-xml[lxml]>=2.11.0
|
|
50
|
+
Requires-Dist: pydantic>=2.8.0
|
|
51
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
52
|
+
Requires-Dist: typer>=0.12.3
|
|
53
|
+
Requires-Dist: tzdata>=2024.1; sys_platform == 'win32'
|
|
54
|
+
Requires-Dist: uvicorn>=0.30.0
|
|
55
|
+
Provides-Extra: dev
|
|
56
|
+
Requires-Dist: httpx>=0.27.0; extra == 'dev'
|
|
57
|
+
Requires-Dist: mypy>=1.11.0; extra == 'dev'
|
|
58
|
+
Requires-Dist: pip-audit>=2.7.3; extra == 'dev'
|
|
59
|
+
Requires-Dist: pytest-asyncio>=0.23.8; extra == 'dev'
|
|
60
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
61
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
62
|
+
Requires-Dist: radon>=6.0.1; extra == 'dev'
|
|
63
|
+
Requires-Dist: ruff>=0.5.5; extra == 'dev'
|
|
64
|
+
Requires-Dist: vulture>=2.11; extra == 'dev'
|
|
65
|
+
Description-Content-Type: text/markdown
|
|
66
|
+
|
|
67
|
+
# MailCadence Engine
|
|
68
|
+
|
|
69
|
+
Production-grade, asynchronous bulk email dispatch and reporting engine built in **Python 3.12+**.
|
|
70
|
+
|
|
71
|
+
## Overview
|
|
72
|
+
|
|
73
|
+
`mail-cadence` is a high-performance email dispatch engine that utilizes SQLite as its primary database backend for runtime configurations and execution history logging, while retaining full XML support for importing and exporting configuration definitions (`tasks.xml` and `schedules.xml`). It evaluates active schedule windows across timezones, dispatches scheduled email tasks concurrently with rate limiting and exponential backoff retry resilience, and sends a final Task Completion Status Report email upon completion.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Key Features
|
|
78
|
+
|
|
79
|
+
- **Python 3.12+ Standards:** Built with strict type safety, `asyncio.TaskGroup`, `StrEnum`, `pydantic v2`, and `pydantic-xml`.
|
|
80
|
+
- **SQLite State Persistence:** SQLite runtime database storing tasks, schedules, report settings, and detailed execution logs with Write-Ahead Logging (`WAL`) mode and busy timeout handling (`30,000 ms`).
|
|
81
|
+
- **Safe XML Import & Export:** Built-in `defusedxml` protection against XXE and entity expansion attacks, supporting bidirectional conversion between XML documents and SQLite data models.
|
|
82
|
+
- **Timezone-Aware Throttling & Idempotency:** Hourly schedule windows with `zoneinfo` time zone awareness, midnight-spanning time calculation, window-level idempotency protection (`last_status == SUCCESS`), and token bucket rate limiting.
|
|
83
|
+
- **Async SMTP Dispatch & Retries:** High-concurrency SMTP dispatch with `aiosmtplib`, `asyncio.Semaphore`, connection pooling, exponential backoff retries on transient errors, and `--dry-run` simulation mode.
|
|
84
|
+
- **Jinja2 Templating:** Templated task emails with path traversal defense and auto-escaped context variables, alongside consolidated HTML/plain-text batch summary reports.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Prerequisites & Installation
|
|
89
|
+
|
|
90
|
+
### Requirements
|
|
91
|
+
- **Python >= 3.12**
|
|
92
|
+
- **pip** package manager
|
|
93
|
+
|
|
94
|
+
### Installation Instructions
|
|
95
|
+
|
|
96
|
+
Clone the repository and install `mail-cadence` along with its development dependencies:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Install in editable mode with development dependencies
|
|
100
|
+
python3 -m pip install -e ".[dev]"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or use the provided `Makefile`:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
make install
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Configuration Reference
|
|
112
|
+
|
|
113
|
+
`mail-cadence` is configured through environment variables (or a `.env` file) for SMTP server options and database location, alongside XML or database settings.
|
|
114
|
+
|
|
115
|
+
### Environment Variables (`Settings`)
|
|
116
|
+
|
|
117
|
+
Supported environment settings defined in `mail_cadence.models.Settings`:
|
|
118
|
+
|
|
119
|
+
| Variable | Default | Description |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `SMTP_HOST` | `localhost` | Hostname or IP address of the SMTP server |
|
|
122
|
+
| `SMTP_PORT` | `1025` | Port number for the SMTP server |
|
|
123
|
+
| `SMTP_USER` | *None* | Username for SMTP authentication (optional) |
|
|
124
|
+
| `SMTP_PASSWORD` | *None* | Password for SMTP authentication (optional) |
|
|
125
|
+
| `SMTP_USE_TLS` | `false` | Enable direct TLS connection (`true`/`false`) |
|
|
126
|
+
| `SMTP_START_TLS` | `false` | Enable STARTTLS command upgrade (`true`/`false`) |
|
|
127
|
+
| `SMTP_TIMEOUT` | `30.0` | Timeout in seconds for SMTP operations |
|
|
128
|
+
| `MAIL_CADENCE_DB` | `mail_cadence.db` | Path to SQLite database file |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## CLI Usage Guide (`mail-cadence`)
|
|
133
|
+
|
|
134
|
+
The package provides the `mail-cadence` CLI executable (as well as `python -m mail_cadence.cli`).
|
|
135
|
+
|
|
136
|
+
### 1. Main Dispatch Command
|
|
137
|
+
|
|
138
|
+
Executes the main bulk mailer dispatch pipeline.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
mail-cadence [OPTIONS]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Flags & Options
|
|
145
|
+
| Option | Short | Type | Default | Description |
|
|
146
|
+
| --- | --- | --- | --- | --- |
|
|
147
|
+
| `--tasks-xml` | `-t` | `PATH` | *None* | Optional path to tasks XML file to seed/upsert database |
|
|
148
|
+
| `--schedule-xml` | `-s` | `PATH` | *None* | Optional path to schedule XML file to seed/upsert database |
|
|
149
|
+
| `--db` | `-b` | `PATH` | `mail_cadence.db` | Path to SQLite database file (overrides `MAIL_CADENCE_DB`) |
|
|
150
|
+
| `--dry-run` | `-d` | `FLAG` | `false` | Execute workflow in simulation mode without sending network SMTP requests |
|
|
151
|
+
| `--verbose` | `-v` | `FLAG` | `false` | Enable verbose debug logging output |
|
|
152
|
+
|
|
153
|
+
#### Exit Codes
|
|
154
|
+
- **`0`**: Successful execution or no tasks were eligible for processing.
|
|
155
|
+
- **`1`**: Configuration or database error encountered (e.g. invalid file format, database connection failure).
|
|
156
|
+
- **`2`**: One or more tasks failed execution during the dispatch batch.
|
|
157
|
+
|
|
158
|
+
#### Example Dispatch Commands
|
|
159
|
+
```bash
|
|
160
|
+
# Execute dispatch and seed XML configurations if database is empty or needs updates
|
|
161
|
+
mail-cadence --tasks-xml config/samples/tasks.xml --schedule-xml config/samples/schedules.xml
|
|
162
|
+
|
|
163
|
+
# Run dispatch in dry-run mode using an existing SQLite database
|
|
164
|
+
mail-cadence --db mail_cadence.db --dry-run --verbose
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### 2. XML Import Command (`import-xml`)
|
|
170
|
+
|
|
171
|
+
Imports task definitions and schedule rules from XML configuration files into the SQLite database.
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
mail-cadence import-xml [OPTIONS]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### Options
|
|
178
|
+
| Option | Short | Type | Default | Description |
|
|
179
|
+
| --- | --- | --- | --- | --- |
|
|
180
|
+
| `--tasks-xml` | `-t` | `PATH` | *None* | Path to tasks configuration XML file |
|
|
181
|
+
| `--schedule-xml` | `-s` | `PATH` | *None* | Path to schedule configuration XML file |
|
|
182
|
+
| `--db` | `-b` | `PATH` | `mail_cadence.db` | Target SQLite database file path |
|
|
183
|
+
| `--mode` | `-m` | `CHOICE` | `upsert` | Import mode: `upsert` (update/insert) or `replace` (clear and reload) |
|
|
184
|
+
|
|
185
|
+
#### Example
|
|
186
|
+
```bash
|
|
187
|
+
mail-cadence import-xml --tasks-xml config/samples/tasks.xml --schedule-xml config/samples/schedules.xml --mode upsert
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### 3. XML Export Command (`export-xml`)
|
|
193
|
+
|
|
194
|
+
Exports current task definitions, schedules, and report settings from the SQLite database back into XML files.
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
mail-cadence export-xml [OPTIONS]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### Options
|
|
201
|
+
| Option | Short | Type | Default | Description |
|
|
202
|
+
| --- | --- | --- | --- | --- |
|
|
203
|
+
| `--tasks-xml` | `-t` | `PATH` | *None* | Destination path for exported tasks XML |
|
|
204
|
+
| `--schedule-xml` | `-s` | `PATH` | *None* | Destination path for exported schedules XML |
|
|
205
|
+
| `--db` | `-b` | `PATH` | `mail_cadence.db` | Source SQLite database file path |
|
|
206
|
+
|
|
207
|
+
#### Example
|
|
208
|
+
```bash
|
|
209
|
+
mail-cadence export-xml --tasks-xml exported_tasks.xml --schedule-xml exported_schedules.xml
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Development & Quality Gates
|
|
215
|
+
|
|
216
|
+
The project maintains strict code quality standards enforced via `Makefile` commands:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Run unit and integration test suite with coverage
|
|
220
|
+
make test
|
|
221
|
+
|
|
222
|
+
# Run code style and linting checks (Ruff & Mypy)
|
|
223
|
+
make lint
|
|
224
|
+
|
|
225
|
+
# Run dead code detection (Vulture)
|
|
226
|
+
make dead-code
|
|
227
|
+
|
|
228
|
+
# Run code complexity analysis (Radon)
|
|
229
|
+
make complexity
|
|
230
|
+
|
|
231
|
+
# Run dependency vulnerability audit (pip-audit)
|
|
232
|
+
make audit
|
|
233
|
+
|
|
234
|
+
# Run all quality gate checks in sequence
|
|
235
|
+
make quality-gate
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Architectural Deep Dive
|
|
241
|
+
|
|
242
|
+
For detailed technical specifications, database entity-relationship diagrams (ERD), scheduling algorithms, Mermaid state/sequence diagrams, and security mechanics, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# MailCadence Engine
|
|
2
|
+
|
|
3
|
+
Production-grade, asynchronous bulk email dispatch and reporting engine built in **Python 3.12+**.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`mail-cadence` is a high-performance email dispatch engine that utilizes SQLite as its primary database backend for runtime configurations and execution history logging, while retaining full XML support for importing and exporting configuration definitions (`tasks.xml` and `schedules.xml`). It evaluates active schedule windows across timezones, dispatches scheduled email tasks concurrently with rate limiting and exponential backoff retry resilience, and sends a final Task Completion Status Report email upon completion.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Key Features
|
|
12
|
+
|
|
13
|
+
- **Python 3.12+ Standards:** Built with strict type safety, `asyncio.TaskGroup`, `StrEnum`, `pydantic v2`, and `pydantic-xml`.
|
|
14
|
+
- **SQLite State Persistence:** SQLite runtime database storing tasks, schedules, report settings, and detailed execution logs with Write-Ahead Logging (`WAL`) mode and busy timeout handling (`30,000 ms`).
|
|
15
|
+
- **Safe XML Import & Export:** Built-in `defusedxml` protection against XXE and entity expansion attacks, supporting bidirectional conversion between XML documents and SQLite data models.
|
|
16
|
+
- **Timezone-Aware Throttling & Idempotency:** Hourly schedule windows with `zoneinfo` time zone awareness, midnight-spanning time calculation, window-level idempotency protection (`last_status == SUCCESS`), and token bucket rate limiting.
|
|
17
|
+
- **Async SMTP Dispatch & Retries:** High-concurrency SMTP dispatch with `aiosmtplib`, `asyncio.Semaphore`, connection pooling, exponential backoff retries on transient errors, and `--dry-run` simulation mode.
|
|
18
|
+
- **Jinja2 Templating:** Templated task emails with path traversal defense and auto-escaped context variables, alongside consolidated HTML/plain-text batch summary reports.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Prerequisites & Installation
|
|
23
|
+
|
|
24
|
+
### Requirements
|
|
25
|
+
- **Python >= 3.12**
|
|
26
|
+
- **pip** package manager
|
|
27
|
+
|
|
28
|
+
### Installation Instructions
|
|
29
|
+
|
|
30
|
+
Clone the repository and install `mail-cadence` along with its development dependencies:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install in editable mode with development dependencies
|
|
34
|
+
python3 -m pip install -e ".[dev]"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or use the provided `Makefile`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
make install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Configuration Reference
|
|
46
|
+
|
|
47
|
+
`mail-cadence` is configured through environment variables (or a `.env` file) for SMTP server options and database location, alongside XML or database settings.
|
|
48
|
+
|
|
49
|
+
### Environment Variables (`Settings`)
|
|
50
|
+
|
|
51
|
+
Supported environment settings defined in `mail_cadence.models.Settings`:
|
|
52
|
+
|
|
53
|
+
| Variable | Default | Description |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `SMTP_HOST` | `localhost` | Hostname or IP address of the SMTP server |
|
|
56
|
+
| `SMTP_PORT` | `1025` | Port number for the SMTP server |
|
|
57
|
+
| `SMTP_USER` | *None* | Username for SMTP authentication (optional) |
|
|
58
|
+
| `SMTP_PASSWORD` | *None* | Password for SMTP authentication (optional) |
|
|
59
|
+
| `SMTP_USE_TLS` | `false` | Enable direct TLS connection (`true`/`false`) |
|
|
60
|
+
| `SMTP_START_TLS` | `false` | Enable STARTTLS command upgrade (`true`/`false`) |
|
|
61
|
+
| `SMTP_TIMEOUT` | `30.0` | Timeout in seconds for SMTP operations |
|
|
62
|
+
| `MAIL_CADENCE_DB` | `mail_cadence.db` | Path to SQLite database file |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## CLI Usage Guide (`mail-cadence`)
|
|
67
|
+
|
|
68
|
+
The package provides the `mail-cadence` CLI executable (as well as `python -m mail_cadence.cli`).
|
|
69
|
+
|
|
70
|
+
### 1. Main Dispatch Command
|
|
71
|
+
|
|
72
|
+
Executes the main bulk mailer dispatch pipeline.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
mail-cadence [OPTIONS]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Flags & Options
|
|
79
|
+
| Option | Short | Type | Default | Description |
|
|
80
|
+
| --- | --- | --- | --- | --- |
|
|
81
|
+
| `--tasks-xml` | `-t` | `PATH` | *None* | Optional path to tasks XML file to seed/upsert database |
|
|
82
|
+
| `--schedule-xml` | `-s` | `PATH` | *None* | Optional path to schedule XML file to seed/upsert database |
|
|
83
|
+
| `--db` | `-b` | `PATH` | `mail_cadence.db` | Path to SQLite database file (overrides `MAIL_CADENCE_DB`) |
|
|
84
|
+
| `--dry-run` | `-d` | `FLAG` | `false` | Execute workflow in simulation mode without sending network SMTP requests |
|
|
85
|
+
| `--verbose` | `-v` | `FLAG` | `false` | Enable verbose debug logging output |
|
|
86
|
+
|
|
87
|
+
#### Exit Codes
|
|
88
|
+
- **`0`**: Successful execution or no tasks were eligible for processing.
|
|
89
|
+
- **`1`**: Configuration or database error encountered (e.g. invalid file format, database connection failure).
|
|
90
|
+
- **`2`**: One or more tasks failed execution during the dispatch batch.
|
|
91
|
+
|
|
92
|
+
#### Example Dispatch Commands
|
|
93
|
+
```bash
|
|
94
|
+
# Execute dispatch and seed XML configurations if database is empty or needs updates
|
|
95
|
+
mail-cadence --tasks-xml config/samples/tasks.xml --schedule-xml config/samples/schedules.xml
|
|
96
|
+
|
|
97
|
+
# Run dispatch in dry-run mode using an existing SQLite database
|
|
98
|
+
mail-cadence --db mail_cadence.db --dry-run --verbose
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### 2. XML Import Command (`import-xml`)
|
|
104
|
+
|
|
105
|
+
Imports task definitions and schedule rules from XML configuration files into the SQLite database.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
mail-cadence import-xml [OPTIONS]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Options
|
|
112
|
+
| Option | Short | Type | Default | Description |
|
|
113
|
+
| --- | --- | --- | --- | --- |
|
|
114
|
+
| `--tasks-xml` | `-t` | `PATH` | *None* | Path to tasks configuration XML file |
|
|
115
|
+
| `--schedule-xml` | `-s` | `PATH` | *None* | Path to schedule configuration XML file |
|
|
116
|
+
| `--db` | `-b` | `PATH` | `mail_cadence.db` | Target SQLite database file path |
|
|
117
|
+
| `--mode` | `-m` | `CHOICE` | `upsert` | Import mode: `upsert` (update/insert) or `replace` (clear and reload) |
|
|
118
|
+
|
|
119
|
+
#### Example
|
|
120
|
+
```bash
|
|
121
|
+
mail-cadence import-xml --tasks-xml config/samples/tasks.xml --schedule-xml config/samples/schedules.xml --mode upsert
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### 3. XML Export Command (`export-xml`)
|
|
127
|
+
|
|
128
|
+
Exports current task definitions, schedules, and report settings from the SQLite database back into XML files.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
mail-cadence export-xml [OPTIONS]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Options
|
|
135
|
+
| Option | Short | Type | Default | Description |
|
|
136
|
+
| --- | --- | --- | --- | --- |
|
|
137
|
+
| `--tasks-xml` | `-t` | `PATH` | *None* | Destination path for exported tasks XML |
|
|
138
|
+
| `--schedule-xml` | `-s` | `PATH` | *None* | Destination path for exported schedules XML |
|
|
139
|
+
| `--db` | `-b` | `PATH` | `mail_cadence.db` | Source SQLite database file path |
|
|
140
|
+
|
|
141
|
+
#### Example
|
|
142
|
+
```bash
|
|
143
|
+
mail-cadence export-xml --tasks-xml exported_tasks.xml --schedule-xml exported_schedules.xml
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Development & Quality Gates
|
|
149
|
+
|
|
150
|
+
The project maintains strict code quality standards enforced via `Makefile` commands:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Run unit and integration test suite with coverage
|
|
154
|
+
make test
|
|
155
|
+
|
|
156
|
+
# Run code style and linting checks (Ruff & Mypy)
|
|
157
|
+
make lint
|
|
158
|
+
|
|
159
|
+
# Run dead code detection (Vulture)
|
|
160
|
+
make dead-code
|
|
161
|
+
|
|
162
|
+
# Run code complexity analysis (Radon)
|
|
163
|
+
make complexity
|
|
164
|
+
|
|
165
|
+
# Run dependency vulnerability audit (pip-audit)
|
|
166
|
+
make audit
|
|
167
|
+
|
|
168
|
+
# Run all quality gate checks in sequence
|
|
169
|
+
make quality-gate
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Architectural Deep Dive
|
|
175
|
+
|
|
176
|
+
For detailed technical specifications, database entity-relationship diagrams (ERD), scheduling algorithms, Mermaid state/sequence diagrams, and security mechanics, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|