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.
Files changed (38) hide show
  1. mail_cadence-0.1.1/.gitignore +110 -0
  2. mail_cadence-0.1.1/AGENTS.md +36 -0
  3. mail_cadence-0.1.1/LICENSE +21 -0
  4. mail_cadence-0.1.1/PKG-INFO +242 -0
  5. mail_cadence-0.1.1/README.md +176 -0
  6. mail_cadence-0.1.1/pyproject.toml +162 -0
  7. mail_cadence-0.1.1/src/mail_cadence/__init__.py +8 -0
  8. mail_cadence-0.1.1/src/mail_cadence/cli.py +410 -0
  9. mail_cadence-0.1.1/src/mail_cadence/database.py +111 -0
  10. mail_cadence-0.1.1/src/mail_cadence/mailer.py +348 -0
  11. mail_cadence-0.1.1/src/mail_cadence/models.py +198 -0
  12. mail_cadence-0.1.1/src/mail_cadence/py.typed +0 -0
  13. mail_cadence-0.1.1/src/mail_cadence/reporter.py +132 -0
  14. mail_cadence-0.1.1/src/mail_cadence/repository.py +629 -0
  15. mail_cadence-0.1.1/src/mail_cadence/scheduler.py +140 -0
  16. mail_cadence-0.1.1/src/mail_cadence/templates/daily_dispatch.html.j2 +5 -0
  17. mail_cadence-0.1.1/src/mail_cadence/templates/report_email.html.j2 +79 -0
  18. mail_cadence-0.1.1/src/mail_cadence/templates/report_email.txt.j2 +26 -0
  19. mail_cadence-0.1.1/src/mail_cadence/templates/settlement_summary.html.j2 +6 -0
  20. mail_cadence-0.1.1/src/mail_cadence/web.py +395 -0
  21. mail_cadence-0.1.1/src/mail_cadence/web_templates/dashboard.html +79 -0
  22. mail_cadence-0.1.1/src/mail_cadence/web_templates/layout.html +42 -0
  23. mail_cadence-0.1.1/src/mail_cadence/web_templates/schedules.html +90 -0
  24. mail_cadence-0.1.1/src/mail_cadence/web_templates/settings.html +38 -0
  25. mail_cadence-0.1.1/src/mail_cadence/web_templates/tasks.html +141 -0
  26. mail_cadence-0.1.1/src/mail_cadence/xml_loader.py +117 -0
  27. mail_cadence-0.1.1/tests/test_cli.py +253 -0
  28. mail_cadence-0.1.1/tests/test_coverage_boost.py +165 -0
  29. mail_cadence-0.1.1/tests/test_database.py +253 -0
  30. mail_cadence-0.1.1/tests/test_e2e_integration.py +87 -0
  31. mail_cadence-0.1.1/tests/test_mailer.py +552 -0
  32. mail_cadence-0.1.1/tests/test_models.py +168 -0
  33. mail_cadence-0.1.1/tests/test_reporter.py +160 -0
  34. mail_cadence-0.1.1/tests/test_roundtrip.py +109 -0
  35. mail_cadence-0.1.1/tests/test_scheduler.py +428 -0
  36. mail_cadence-0.1.1/tests/test_smtp_e2e_integration.py +328 -0
  37. mail_cadence-0.1.1/tests/test_web.py +277 -0
  38. 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).